NaturalOrderComparator implements Comparator<String> { @Override public int compare(String a, String b) { int ia = 0, ib = 0; int nza = 0, nzb = 0; char ca, cb; int result; while (true) { nza = nzb = 0; ca = charAt(a, ia); cb = charAt(b, ib); while (Character.isSpaceChar(ca) || ca == '0') { if (ca == '0') { nza++; } else { nza = 0; } ca = charAt(a, ++ia); } while (Character.isSpaceChar(cb) || cb == '0') { if (cb == '0') { nzb++; } else { nzb = 0; } cb = charAt(b, ++ib); } if (Character.isDigit(ca) && Character.isDigit(cb)) { if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) { return result; } do { ia++; ib++; boolean aDigitsDone = ia >= a.length() || !Character.isDigit(charAt(a, ia)); boolean bDigitsDone = ib >= b.length() || !Character.isDigit(charAt(b, ib)); if (aDigitsDone != bDigitsDone) { throw new IllegalStateException("Digit sequence lengths should have matched, '"+a+"' v '"+b+"'"); } if (aDigitsDone) break; } while (true); if (ia < a.length() || ib < b.length()) { if (ia >= a.length()) return -1; if (ib >= b.length()) return 1; if ((result = compare(a.substring(ia), b.substring(ib))) != 0) { return result; } } return nzb-nza; } else if ((Character.isDigit(ca) || nza>0) && (Character.isDigit(cb) || nzb>0)) { if (nza==0) { return 1; } if (nzb==0) { return -1; } } if (ca == 0 && cb == 0) { return nza - nzb; } if (ca < cb) { return -1; } else if (ca > cb) { return +1; } ++ia; ++ib; } }  @Override int compare(String a, String b); static final NaturalOrderComparator INSTANCE; }
@Test public void testNoc() { Assert.assertEquals(noc.compare("0", "1"), -1); Assert.assertEquals(noc.compare("1", "10"), -1); Assert.assertEquals(noc.compare("9", "10"), -1); Assert.assertEquals(noc.compare("a", "b"), -1); Assert.assertEquals(noc.compare("a9", "a10"), -1); Assert.assertEquals(noc.compare("1", "02"), -1); Assert.assertEquals(noc.compare("02", "3"), -1); Assert.assertEquals(noc.compare("02", "2"), -1); Assert.assertEquals(noc.compare("1.b", "02.a"), -1); Assert.assertEquals(noc.compare("02.b", "3.a"), -1); Assert.assertEquals(noc.compare("02.a", "2.b"), -1); Assert.assertEquals(noc.compare("0.9", "0.91"), -1); Assert.assertEquals(noc.compare("0.90", "0.91"), -1); Assert.assertEquals(noc.compare("1.2.x", "1.09.x"), -1); Assert.assertEquals(noc.compare("2", "2.1"), -1); Assert.assertEquals(noc.compare("2", "2.01"), -1); Assert.assertEquals(noc.compare("2.01", "2.1"), -1); Assert.assertEquals(noc.compare("2.1", "2.02"), -1); Assert.assertEquals(noc.compare("2", "02.1"), -1); Assert.assertEquals(noc.compare("2", "02.01"), -1); Assert.assertEquals(noc.compare("0", "1a"), -1); Assert.assertEquals(noc.compare("0a", "1"), -1); }
@Test public void testBasicOnes() { Assert.assertEquals(0, noc.compare("a", "a")); Assert.assertTrue(noc.compare("a", "b") < 0); Assert.assertTrue(noc.compare("b", "a") > 0); Assert.assertTrue(noc.compare("9", "10") < 0); Assert.assertTrue(noc.compare("10", "9") > 0); Assert.assertTrue(noc.compare("b10", "a9") > 0); Assert.assertTrue(noc.compare("b9", "a10") > 0); Assert.assertTrue(noc.compare(" 9", "10") < 0); Assert.assertTrue(noc.compare("10", " 9") > 0); }
@Test public void testVersionNumbers() { Assert.assertEquals(noc.compare("10.5.8", "10.5.8"), 0); Assert.assertTrue(noc.compare("10.5", "9.9") > 0); Assert.assertTrue(noc.compare("10.5.1", "10.5") > 0); Assert.assertTrue(noc.compare("10.5.1", "10.6") < 0); Assert.assertTrue(noc.compare("10.5.1-1", "10.5.1-0") > 0); }
@Test public void testWordsNumsPunctuation() { Assert.assertTrue(noc.compare("1", "") > 0); Assert.assertTrue(noc.compare("one", "1") > 0); Assert.assertTrue(noc.compare("some1", "some") > 0); Assert.assertTrue(noc.compare("someone", "some") > 0); Assert.assertTrue(noc.compare("someone", "some1") > 0); Assert.assertTrue(noc.compare("someone", "some0") > 0); Assert.assertTrue(noc.compare("some-one", "some-1") > 0); Assert.assertTrue(noc.compare("a1", "aa1") < 0); Assert.assertTrue(noc.compare("a", "aa") < 0); Assert.assertTrue(noc.compare("a", "a-") < 0); Assert.assertTrue(noc.compare("a-", "a.") < 0); Assert.assertTrue(noc.compare("a-", "a1") < 0); Assert.assertTrue(noc.compare("a-", "aa") < 0); Assert.assertTrue(noc.compare("a1", "aa") < 0); Assert.assertTrue(noc.compare("aA", "a_") < 0); Assert.assertTrue(noc.compare("a_", "aa") < 0); Assert.assertTrue(noc.compare("a-1", "a1") < 0); Assert.assertTrue(noc.compare("a0", "a-1") < 0); Assert.assertTrue(noc.compare("a0", "aa0") < 0); Assert.assertTrue(noc.compare("a-9", "a-10") < 0); Assert.assertTrue(noc.compare("a-0", "a-01") < 0); Assert.assertTrue(noc.compare("a-01", "a-1") < 0); }