# HG changeset patch # User igerasim # Date 1503988648 25200 # Mon Aug 28 23:37:28 2017 -0700 # Node ID 62137618d00310efaa3545ced9fa2d2cade487d8 # Parent ebdaa1e7ab33aa87921e32bc3a5f55834da49872 [mq]: 8134512-provide-Alpha-Numeric-logical-Comparator diff --git a/src/java.base/share/classes/java/util/Comparator.java b/src/java.base/share/classes/java/util/Comparator.java --- a/src/java.base/share/classes/java/util/Comparator.java +++ b/src/java.base/share/classes/java/util/Comparator.java @@ -30,7 +30,6 @@ import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; import java.util.function.ToDoubleFunction; -import java.util.Comparators; /** * A comparison function, which imposes a total ordering on some @@ -526,9 +525,162 @@ * @throws NullPointerException if the argument is null * @since 1.8 */ - public static Comparator comparingDouble(ToDoubleFunction keyExtractor) { + public static Comparator comparingDouble(ToDoubleFunction keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator & Serializable) (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2)); } + + /** + * The returned comparator compares two character sequences as though each + * of them would be first transformed into a tuple of the form: + *
{@code (A0, N0, A1, N1, ..., An-1, Nn-1, An, Nn)}
+ * where: + *

{@code A0} and {@code An} are (possibly empty) sub-sequences + * consisting of non-decimal-digit characters, + *

{@code A1 ... An-1} are non-empty sub-sequences consisting of + * non-decimal-digit characters, + *

{@code N0 ... Nn-1} are non-empty sub-sequences consisting of + * decimal-digit characters, and + *

{@code Nn} is a (possibly empty) sub-sequence consisting of + * decimal-digit characters. + * + *

All sub-sequences concatenated together in order as they appear in the + * tuple yield the original character sequence. + * + * After transformation, the tuples are compared by their elements (from + * left to right) so that corresponding {@code Ax} elements are compared + * using the provided comparator {@code alphaComparator} and {@code Nx} + * elements are compared as non negative decimal integers. + * + * The first pair of compared elements that is different with respect to the + * used comparator (either {@code alphaComparator}, or special decimal + * comparator) if any, provides the result produced by this comparator. + * The arguments are treated equal, if and only if all the subsequences, + * both decimal and non-decimal, compare equal. + * + *

For example, the following array was sorted using such comparator: + *

{@code
+     * { "1ab", "5ab", "10ab",
+     *   "a1b", "a5b", "a10b",
+     *   "ab1", "ab5", "ab10" };}
+ * + *

When comparing numerical parts, an empty character sequence is + * considered less than any non-empty sequence of decimal digits. + * + *

If the numeric values of two compared character sub-sequences are + * equal, but their string representations have different number of leading + * zeroes, the comparator treats the number with less leading zeros as + * smaller. + * For example, {@code "abc 1" < "abc 01" < "abc 001"}. + * + * @apiNote For example, to sort a collection of {@code String} based on + * case-insensitive ordering, and treating numbers with more leading + * zeroes as greater, one could use + * + *

{@code
+     *     Comparator cmp = Comparator.comparingAlphaDecimal(
+     *             Comparator.comparing(CharSequence::toString,
+     *                                  String::compareToIgnoreCase));
+     * }
+ * + * @implSpec To test if the given code point represents a decimal digit, + * the comparator checks if {@link java.lang.Character#getType(int)} + * returns value {@link java.lang.Character#DECIMAL_DIGIT_NUMBER}. + * The comparator uses {@link java.lang.Character#digit(int, int)} with + * the second argument set to {@code 10} to determine the numeric + * value of a digit represented by the given code point. + * + * @param alphaComparator the comparator that compares sub-sequences + * consisting of non-decimal-digits + * @param the type of elements to be compared; normally + * {@link java.lang.CharSequence} + * @return a comparator that compares character sequences, following the + * rules described above + * @throws NullPointerException if the argument is null + * + * @since 10 + */ + public static Comparator + comparingAlphaDecimal(Comparator alphaComparator) { + return new Comparators.AlphaDecimalComparator<>( + Objects.requireNonNull(alphaComparator), false); + } + + /** + * The returned comparator compares two character sequences as though each + * of them would be first transformed into a tuple of the form: + *
{@code (A0, N0, A1, N1, ..., An-1, Nn-1, An, Nn)}
+ * where: + *

{@code A0} and {@code An} are (possibly empty) sub-sequences + * consisting of non-decimal-digit characters, + *

{@code A1 ... An-1} are non-empty sub-sequences consisting of + * non-decimal-digit characters, + *

{@code N0 ... Nn-1} are non-empty sub-sequences consisting of + * decimal-digit characters, and + *

{@code Nn} is a (possibly empty) sub-sequence consisting of + * decimal-digit characters. + * + *

All sub-sequences concatenated together in order as they appear in the + * tuple yield the original character sequence. + * + * After transformation, the tuples are compared by their elements (from + * left to right) so that corresponding {@code Ax} elements are compared + * using the provided comparator {@code alphaComparator} and {@code Nx} + * elements are compared as non negative decimal integers. + * + * The first pair of compared elements that is different with respect to the + * used comparator (either {@code alphaComparator}, or special decimal + * comparator) if any, provides the result produced by this comparator. + * The arguments are treated equal, if and only if all the subsequences, + * both decimal and non-decimal, compare equal. + * + *

For example, the following array was sorted using such comparator: + *

{@code
+     * { "1ab", "5ab", "10ab",
+     *   "a1b", "a5b", "a10b",
+     *   "ab1", "ab5", "ab10" };}
+ * + *

When comparing numerical parts, an empty character sequence is + * considered less than any non-empty sequence of decimal digits. + * + *

If the numeric values of two compared character sub-sequences are + * equal, but their string representations have different number of leading + * zeroes, the comparator treats the number with more leading zeros as + * smaller. + * For example, {@code "abc 001" < "abc 01" < "abc 1"}. + * + * @apiNote For example, to sort a collection of {@code String} based on + * case-insensitive ordering, and treating numbers with less leading + * zeroes as greater, one could use + * + *

{@code
+     *       Comparator cmp = Comparator.comparingAlphaDecimalLeadingZeroesFirst(
+     *             Comparator.comparing(CharSequence::toString,
+     *                                  String::compareToIgnoreCase));
+     * }
+ * + * @implSpec To test if the given code point represents a decimal digit, + * the comparator checks if {@link java.lang.Character#getType(int)} + * returns value {@link java.lang.Character#DECIMAL_DIGIT_NUMBER}. + * The comparator uses {@link java.lang.Character#digit(int, int)} with + * the second argument set to {@code 10} to determine the numeric + * value of a digit represented by the given code point. + * + * @param alphaComparator the comparator that compares sub-sequences + * consisting of non-decimal-digits + * @param the type of elements to be compared; normally + * {@link java.lang.CharSequence} + * @return a comparator that compares character sequences, following the + * rules described above + * @throws NullPointerException if the argument is null + * + * @since 10 + */ + public static Comparator + comparingAlphaDecimalLeadingZeroesFirst( + Comparator alphaComparator) { + return new Comparators.AlphaDecimalComparator<>( + Objects.requireNonNull(alphaComparator), true); + } } diff --git a/src/java.base/share/classes/java/util/Comparators.java b/src/java.base/share/classes/java/util/Comparators.java --- a/src/java.base/share/classes/java/util/Comparators.java +++ b/src/java.base/share/classes/java/util/Comparators.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,4 +95,203 @@ return new NullComparator<>(!nullFirst, real == null ? null : real.reversed()); } } + + /** + * The comparator for comparing character sequences that consist solely + * of decimal digits. The result of comparing is as if the values were + * compared numerically. + */ + private static class DecimalComparator implements Comparator, + Serializable { + + private static final long serialVersionUID = 0x661e8cc550c7704dL; + + private static final Comparator + DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST = + new DecimalComparator(true) { + @Override + public Comparator reversed() { + return DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST_REVERSED; + } + }; + + private static final Comparator + DECIMAL_COMPARATOR_LEADING_ZEROES_LAST = + new DecimalComparator(false) { + @Override + public Comparator reversed() { + return DECIMAL_COMPARATOR_LEADING_ZEROES_LAST_REVERSED; + } + }; + + private static final Comparator + DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST_REVERSED = + new DecimalComparator(true) { + @Override + public Comparator reversed() { + return DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST; + } + @Override + public int compare(CharSequence cs1, CharSequence cs2) { + return super.compare(cs2, cs1); + } + }; + + private static final Comparator + DECIMAL_COMPARATOR_LEADING_ZEROES_LAST_REVERSED = + new DecimalComparator(false) { + @Override + public Comparator reversed() { + return DECIMAL_COMPARATOR_LEADING_ZEROES_LAST; + } + @Override + public int compare(CharSequence cs1, CharSequence cs2) { + return super.compare(cs2, cs1); + } + }; + + private final boolean leadingZeroesFirst; + + private DecimalComparator(boolean leadingZeroesFirst) { + this.leadingZeroesFirst = leadingZeroesFirst; + } + + static Comparator getInstance(boolean leadingZeroesFirst) { + return leadingZeroesFirst ? DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST + : DECIMAL_COMPARATOR_LEADING_ZEROES_LAST; + } + + private boolean canSkipLeadingZeroes(CharSequence s, int len) { + for (int i = 0; i < len;) { + int cp = Character.codePointAt(s, i); + if (Character.digit(cp, 10) != 0) + return false; + i += Character.charCount(cp); + } + return true; + } + + @Override + public int compare(CharSequence cs1, CharSequence cs2) { + int len1 = Character.codePointCount(cs1, 0, cs1.length()); + int len2 = Character.codePointCount(cs2, 0, cs2.length()); + int dlen = len1 - len2; + if (len1 == 0 || len2 == 0) { + return dlen; + } else if (dlen > 0) { + if (!canSkipLeadingZeroes(cs1, dlen)) + return 1; + int off = Character.offsetByCodePoints(cs1, 0, dlen); + cs1 = cs1.subSequence(off, cs1.length()); + } else if (dlen < 0) { + if (!canSkipLeadingZeroes(cs2, -dlen)) + return -1; + int off = Character.offsetByCodePoints(cs2, 0, -dlen); + cs2 = cs2.subSequence(off, cs2.length()); + } + int cmp = 0; + for (int i1 = 0, i2 = 0; i1 < cs1.length(); ) { + int cp1 = Character.codePointAt(cs1, i1); + int cp2 = Character.codePointAt(cs2, i2); + if (cp1 != cp2) { + if (cmp == 0) { + cmp = cp1 - cp2; + } + int cmpNum = Character.digit(cp1, 10) - + Character.digit(cp2, 10); + if (cmpNum != 0) { + return cmpNum; + } + } + i1 += Character.charCount(cp1); + i2 += Character.charCount(cp2); + } + return dlen == 0 ? cmp : (leadingZeroesFirst ^ (dlen < 0) ? -1 : 1); + } + } + + /** + * Compares char sequences, taking into account their numeric part if one + * exists. + * + * @see Comparator#comparingAlphaDecimal(Comparator) + * @see Comparator#comparingAlphaDecimalLeadingZeroesFirst(Comparator) + */ + static class AlphaDecimalComparator + implements Comparator, Serializable { + + private static final long serialVersionUID = 0xd7a7129b9ca1056dL; + + private final Comparator alphaComparator; + private final Comparator decimalComparator; + + AlphaDecimalComparator(Comparator alphaComparator, + boolean leadingZeroesFirst) { + this(alphaComparator, DecimalComparator.getInstance(leadingZeroesFirst)); + } + + private AlphaDecimalComparator(Comparator alphaComparator, + Comparator decimalComparator) { + this.alphaComparator = alphaComparator; + this.decimalComparator = decimalComparator; + } + + @Override + public Comparator reversed() { + return new AlphaDecimalComparator<>(alphaComparator.reversed(), + decimalComparator.reversed()); + } + + @Override + public int compare(T cs1, T cs2) { + Decomposer d1 = new Decomposer(cs1); + Decomposer d2 = new Decomposer(cs2); + for (;;) { + int cmp; + if ((cmp = alphaComparator.compare(d1.get(), d2.get())) != 0 || + (cmp = decimalComparator.compare(d1.get(), d2.get())) != 0) + { + return cmp; + } + if (d1.eos() && d2.eos()) return 0; + } + } + + /** + * Given a CharSequence, splits it into a series of subsequences so that + * every character of the very first subsequence (possibly empty) is + * not a decimal digit; then every character of the second subsequence + * is a decimal digit, and so on. + */ + private static class Decomposer { + private final CharSequence sequence; + private boolean expectingDecimal = false; + private int index = 0; + + Decomposer(CharSequence sequence) { + this.sequence = sequence; + } + + CharSequence get() { + int start = index, end = start, len = sequence.length() - start; + while (len > 0) { + int cp = Character.codePointAt(sequence, end); + int ct = Character.getType(cp); + boolean isDecimal = (ct == Character.DECIMAL_DIGIT_NUMBER); + if (isDecimal ^ expectingDecimal) { + break; + } + int cpWidth = Character.charCount(cp); + end += cpWidth; + len -= cpWidth; + } + expectingDecimal = !expectingDecimal; + return sequence.subSequence(start, index = end); + } + + boolean eos() { + return index >= sequence.length(); + } + } + } } diff --git a/test/java/util/Comparator/BasicTest.java b/test/java/util/Comparator/BasicTest.java --- a/test/java/util/Comparator/BasicTest.java +++ b/test/java/util/Comparator/BasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,14 +23,21 @@ /** * @test + * @bug 8134512 * @summary Comparator default method tests * @run testng BasicTest */ -import java.util.TreeMap; -import java.util.Comparator; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Random; +import java.util.TreeMap; import java.util.function.Function; import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; @@ -366,4 +373,235 @@ fail("comparing(null) should throw NPE"); } catch (NullPointerException npe) {} } + + @Test(dataProvider = "presorted") + public void testAlphaDecimal(String[] sorted, Comparator cmp) { + for (int i = 0; i < sorted.length; ++i) { + for (int j = 0; j < sorted.length; ++j) { + int res = cmp.compare(sorted[i], sorted[j]); + boolean ok = (res == 0 && i == j) || + (res <= 0 && i < j) || + (res >= 0 && i > j); + assertTrue(ok); + if (res == 0 && i != j) { + res = cmp.compare(sorted[j], sorted[i]); + assertTrue(res == 0); + } + } + } + } + + // Simple case: sorting 1 .. 11 + private static String[] arr0 = new String[] { + "java 1", "java 2", "java 3", + "java 4", "java 5", "java 6", + "java 7", "java 8", "java 9", + "java 10", "java 11", "java 11" }; + + // Leading zeros and multiple numeric parts + private static String[] arr1 = new String[] { + "string", "string0", "string00", + "string1", "string01", "string001", + "string2", "string02", "string002", + "string002.a", "string002.a0", "string002.a1", + "string0002", "string0002", + "string1234", "string10234", + "string100234", "string100234", + "string1000234", "string1000234" }; + + // Leading zeros first + private static String[] arr2 = new String[] { + "string", "string00", "string0", + "string001", "string01", "string1", + "string0002", "string0002", "string002", + "string002.a", "string002.a0", "string002.a1", + "string02", "string02", "string2", "string2" }; + + // Sample from MSDN + private static String[] arr3 = new String[] { + "2string", "3string", "20string", + "st2ring", "st3ring", "st20ring", + "string2", "string3", "string20" }; + + // Very long numbers + private static String[] arr4 = new String[] { + "q_0", + "q_1", + "q_1_", + "q_10000000000000000000000000000000000000000", + "q_10000000000000000000000000000000000000000", + "q_20000000000000000000000000000000000000000", + "q_100000000000000000000000000000000000000000", + "q_500000000000000000000000000000000000000000", + "q_10000000000000000000000000000000000000000000000000000000000000000000000000001", + "q_10000000000000000000000000000000000000000000000000000000000000000000000000001", + "q_20000000000000000000000000000000000000000000000000000000000000000000000000000", + "q_20000000000000000000000000000000000000000000000000000000000000000000000000001", + "q_200000000000000000000000000000000000000000000000000000000000000000000000000000", + "y_1", + "y_10000000000000000000000000000000000000000000000000000000000000000000000000000" }; + + // Plain numbers + private static String[] arr5 = new String[] { + "", + "", + "0", + "1", + "2", + "10", + "10", + "11", + "15", + "19", + "20", + "90", + "100", + "101", + "1024", + "2042", + "2147483647", + "2147483648", // Integer.MAX_VALUE + "2147483649", + "4294967296", + "9223372036854775806", + "9223372036854775807", // Long.MAX_VALUE + "9223372036854775808", + "18446744073709551615", + "18446744073709551616", + "8000000000000000000000000000000000000000000", + "10000000000000000000000000000000000000000500", + "10000000000000000000000000000000000000000500", + "10000000000000000000000000000000000000000501", + "20000000000000000000000000000000000000000500", + "20000000000000000000700000000000000000000000", + }; + + // Plain alphabetical strings + private static String[] arr6 = new String[] { + "", + "a", + "a", + "abcd", + "abcdefgh", + "b", + "bcd", + "bcdefgh", + "z", + "z ", + "zabcd", + "zbcd", + "zcd", + "zz", + }; + + // Case insensitive + private static String[] arr7 = new String[] { + "", + "5", + "42", + "309", + "a", + "A", + "a 1", + "A 2", + "a 15", + "A 21", + "case 42 Insensitive", + "CaSe 42 inSEnsItiVe", + "CAsE 42 INSENSitive", + "caSe 42 INSENSITIVE", + "caSe 42 INSENSITIVE", + }; + + private static String[] replaceDigits(String[] arr, int zeroCp) { + return Arrays.asList(arr) + .stream() + .map(str -> { + StringBuilder sb = new StringBuilder(); + int len = Character.codePointCount(str, 0, str.length()); + for (int i = 0; i < len; ) { + int cp = str.codePointAt(i); + i += Character.charCount(cp); + if (Character.isDigit(cp)) + cp = zeroCp + Character.digit(cp, 10); + sb.appendCodePoint(cp); + } + return sb.toString(); + } + ).toArray(String[]::new); + } + + @DataProvider(name = "presorted") + public Object[][] createPresortedArrays() { + String[][] rev = { + arr0.clone(), arr1.clone(), arr2.clone(), arr3.clone(), + arr4.clone(), arr5.clone(), arr6.clone(), arr7.clone(), + }; + for (String[] r : rev) { + Collections.reverse(Arrays.asList(r)); + } + Comparator naturalOrder = Comparator.comparing( + CharSequence::toString, Comparator.naturalOrder()); + Comparator caseInsOrder = Comparator.comparing( + CharSequence::toString, String::compareToIgnoreCase); + int[] digitRangeStart = { + 0x0030, // Latin1 + 0x0660, // Arabic-Indic + 0x06f0, // Extended Arabic-Indic + 0x0b66, // Oriya + 0x0e50, // Thai + 0xa9d0, // Javanese + 0xff10, // Fullwidth + 0x104a0, // Osmanya + 0x118e0, // Warang + 0x1d7ce, // Mathematical bold + 0x1d7d8, // Mathematical double-struck + 0x1d7e2, // Mathematical Sans-serif + 0x1d7ec, // Mathematical Sans-serif bold + 0x1d7f6, // Mathematical Monospace + }; + List res = new ArrayList<>(); + for (int i = 0; i < digitRangeStart.length; ++i) { + res.add(new Object[] {replaceDigits(arr0, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr0, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed().reversed()}); + res.add(new Object[] {replaceDigits(arr0, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr0, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed().reversed()}); + res.add(new Object[] {replaceDigits(arr1, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr1, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed().reversed().reversed().reversed()}); + res.add(new Object[] {replaceDigits(arr2, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr2, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed().reversed().reversed().reversed()}); + res.add(new Object[] {replaceDigits(arr3, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr3, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr4, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr4, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr5, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr5, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed().reversed()}); + res.add(new Object[] {replaceDigits(arr5, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr5, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed().reversed()}); + res.add(new Object[] {replaceDigits(arr6, digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr6, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder)}); + res.add(new Object[] {replaceDigits(arr7, digitRangeStart[i]), Comparator.comparingAlphaDecimal(caseInsOrder)}); + res.add(new Object[] {replaceDigits(arr7, digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(caseInsOrder)}); + res.add(new Object[] {replaceDigits(rev[0], digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[0], digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed().reversed().reversed()}); + res.add(new Object[] {replaceDigits(rev[0], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[0], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed().reversed().reversed()}); + res.add(new Object[] {replaceDigits(rev[1], digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[2], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[3], digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[3], digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed().reversed().reversed().reversed().reversed()}); + res.add(new Object[] {replaceDigits(rev[3], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[3], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed().reversed().reversed().reversed().reversed()}); + res.add(new Object[] {replaceDigits(rev[4], digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[4], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[5], digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[5], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[6], digitRangeStart[i]), Comparator.comparingAlphaDecimal(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[6], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(naturalOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[7], digitRangeStart[i]), Comparator.comparingAlphaDecimal(caseInsOrder).reversed()}); + res.add(new Object[] {replaceDigits(rev[7], digitRangeStart[i]), Comparator.comparingAlphaDecimal(caseInsOrder).reversed().reversed().reversed()}); + res.add(new Object[] {replaceDigits(rev[7], digitRangeStart[i]), Comparator.comparingAlphaDecimalLeadingZeroesFirst(caseInsOrder).reversed()}); + } + return res.toArray(new Object[res.size()][]); + } }