Ergänzung zu der Antwort von @stanislav . Einige Probleme, mit denen ich bei der Verwendung der Antwort konfrontiert war, waren:
- Groß- und Kleinbuchstaben werden durch die Zeichen zwischen ihren ASCII-Codes getrennt. Dies unterbricht den Fluss, wenn die zu sortierenden Zeichenfolgen _ oder andere Zeichen enthalten, die in ASCII zwischen Kleinbuchstaben und Großbuchstaben liegen.
- Wenn zwei Zeichenfolgen gleich sind, außer dass die Anzahl der führenden Nullen unterschiedlich ist, gibt die Funktion 0 zurück, wodurch die Sortierung von den ursprünglichen Positionen der Zeichenfolge in der Liste abhängt.
Diese beiden Probleme wurden im neuen Code behoben. Und ich habe ein paar Funktionen anstelle einiger sich wiederholender Codes gemacht. Die Variable differentCaseCompared verfolgt, ob zwei Zeichenfolgen identisch sind, mit Ausnahme der unterschiedlichen Fälle. In diesem Fall wird der Wert der ersten subtrahierten Groß- und Kleinschreibung zurückgegeben. Dies geschieht, um das Problem zu vermeiden, dass zwei Zeichenfolgen, die sich je nach Groß- und Kleinschreibung unterscheiden, als 0 zurückgegeben werden.
public class NaturalSortingComparator implements Comparator<String> {
@Override
public int compare(String string1, String string2) {
int lengthOfString1 = string1.length();
int lengthOfString2 = string2.length();
int iteratorOfString1 = 0;
int iteratorOfString2 = 0;
int differentCaseCompared = 0;
while (true) {
if (iteratorOfString1 == lengthOfString1) {
if (iteratorOfString2 == lengthOfString2) {
if (lengthOfString1 == lengthOfString2) {
return differentCaseCompared;
}
else {
return lengthOfString1 - lengthOfString2;
}
}
else
return -1;
}
if (iteratorOfString2 == lengthOfString2) {
return 1;
}
char ch1 = string1.charAt(iteratorOfString1);
char ch2 = string2.charAt(iteratorOfString2);
if (Character.isDigit(ch1) && Character.isDigit(ch2)) {
iteratorOfString1 = skipLeadingZeroes(string1, lengthOfString1, iteratorOfString1);
iteratorOfString2 = skipLeadingZeroes(string2, lengthOfString2, iteratorOfString2);
int endPositionOfNumbersInString1 = findEndPositionOfNumber(string1, lengthOfString1, iteratorOfString1);
int endPositionOfNumbersInString2 = findEndPositionOfNumber(string2, lengthOfString2, iteratorOfString2);
int lengthOfDigitsInString1 = endPositionOfNumbersInString1 - iteratorOfString1;
int lengthOfDigitsInString2 = endPositionOfNumbersInString2 - iteratorOfString2;
if (lengthOfDigitsInString1 != lengthOfDigitsInString2)
return lengthOfDigitsInString1 - lengthOfDigitsInString2;
while (iteratorOfString1 < endPositionOfNumbersInString1) {
if (string1.charAt(iteratorOfString1) != string2.charAt(iteratorOfString2))
return string1.charAt(iteratorOfString1) - string2.charAt(iteratorOfString2);
iteratorOfString1++;
iteratorOfString2++;
}
} else {
if (ch1 != ch2) {
if (!ignoreCharacterCaseEquals(ch1, ch2))
return Character.toLowerCase(ch1) - Character.toLowerCase(ch2);
if (differentCaseCompared == 0) {
differentCaseCompared = ch1 - ch2;
}
}
iteratorOfString1++;
iteratorOfString2++;
}
}
}
private boolean ignoreCharacterCaseEquals(char character1, char character2) {
return Character.toLowerCase(character1) == Character.toLowerCase(character2);
}
private int findEndPositionOfNumber(String string, int lengthOfString, int end) {
while (end < lengthOfString && Character.isDigit(string.charAt(end)))
end++;
return end;
}
private int skipLeadingZeroes(String string, int lengthOfString, int iteratorOfString) {
while (iteratorOfString < lengthOfString && string.charAt(iteratorOfString) == '0')
iteratorOfString++;
return iteratorOfString;
}
}
Das Folgende ist ein Unit-Test, den ich verwendet habe.
public class NaturalSortingComparatorTest {
private int NUMBER_OF_TEST_CASES = 100000;
@Test
public void compare() {
NaturalSortingComparator naturalSortingComparator = new NaturalSortingComparator();
List<String> expectedStringList = getCorrectStringList();
List<String> testListOfStrings = createTestListOfStrings();
runTestCases(expectedStringList, testListOfStrings, NUMBER_OF_TEST_CASES, naturalSortingComparator);
}
private void runTestCases(List<String> expectedStringList, List<String> testListOfStrings,
int numberOfTestCases, Comparator<String> comparator) {
for (int testCase = 0; testCase < numberOfTestCases; testCase++) {
Collections.shuffle(testListOfStrings);
testListOfStrings.sort(comparator);
Assert.assertEquals(expectedStringList, testListOfStrings);
}
}
private List<String> getCorrectStringList() {
return Arrays.asList(
"1", "01", "001", "2", "02", "10", "10", "010",
"20", "100", "_1", "_01", "_2", "_200", "A 02",
"A01", "a2", "A20", "t1A", "t1a", "t1AB", "t1Ab",
"t1aB", "t1ab", "T010T01", "T0010T01");
}
private List<String> createTestListOfStrings() {
return Arrays.asList(
"10", "20", "A20", "2", "t1ab", "01", "T010T01", "t1aB",
"_2", "001", "_200", "1", "A 02", "t1Ab", "a2", "_1", "t1A", "_01",
"100", "02", "T0010T01", "t1AB", "10", "A01", "010", "t1a");
}
}
Vorschläge willkommen! Ich bin nicht sicher, ob das Hinzufügen der Funktionen etwas anderes als den Lesbarkeitsteil der Dinge ändert.
PS: Es tut uns leid, eine weitere Antwort auf diese Frage hinzuzufügen. Aber ich habe nicht genug Wiederholungen, um die Antwort zu kommentieren, die ich für meine Verwendung geändert habe.