Benutzerdefinierte Sortierung so, dass A vor a und B vor b steht

11

Ich habe eine Liste solcher Farben:

Pink, Blau, Rot, Blau, Grau, Grün, Lila, Schwarz ... usw.

List<String> listOfColors =  Arrays.asList("Pink", "Blue", "Red", "blue", "Grey", "green", "purple", "black");

Es gibt einige Zwischenoperationen wie das Filtern einiger Fruchtfarben. Jetzt habe ich gefilterte Ergebnisse, in denen sie sortiert werden sollen:

Blau, Schwarz, Blau, Grau, Grün, Pink, Lila, Rot

Ich habe versucht :

List<String> collect = listOfColors.stream().sorted(String::compareToIgnoreCase)
        .collect(Collectors.toList());

Es funktioniert nicht wie erwartet.

Die Ausgabe ist die folgende:

schwarz, blau, blau, grün, grau, pink, lila, rot

Ich möchte folgendes:

Blau, Schwarz, Blau, Grau, Grün, Pink, Lila, Rot

Vishwa Ratna
quelle
2
Sollte Schwarz nicht vor Blau und Grün vor Grau kommen?
Ravindra Ranwala
3
aist vorher uso ist das Ergebnis korrekt
Jens
2
@ RavindraRanwala, Blue 's B ist das Kapital, Back 's B jedoch nicht.
Vishwa Ratna
2
Ich habe es versucht, aber es gibt nicht diese spezifische Reihenfolge. Es gibt [black, Blue, blue, green, Grey, Pink, purple, Red]@ chrylis-onstrike-
Ravindra Ranwala
2
Wenn Sie möchten, dass das Großbuchstaben vor dem unteren Gehäuse steht, ist das Ignorieren des Gehäuses das Letzte, was Sie möchten.
Teepeemm

Antworten:

8

Meine Lösung besteht darin, die Sortierung in zwei Schritten mithilfe der Comparator.thenComparing()Methode zu verwenden.

Vergleichen Sie zunächst die Zeichenfolgen nur mit dem ersten Zeichen, wobei die Groß- und Kleinschreibung ignoriert wird. Daher bleiben die Gruppen mit demselben ersten Zeichen (egal in welchem ​​Fall) bisher unsortiert. Wenden Sie dann im zweiten Schritt die normale alphabetische Sortierung an, um diese unsortierten Untergruppen zu sortieren.

List<String> listOfColors =  Arrays.asList("Pink", "Blue", "Red", "blue", "Grey", "green", "purple", "black");
Comparator<String> comparator = Comparator.comparing(s -> 
        Character.toLowerCase(s.charAt(0)));
listOfColors.sort(comparator.thenComparing(Comparator.naturalOrder()));
System.out.println(listOfColors);

Vielleicht kann es noch optimiert werden, aber es gibt das gewünschte Ergebnis:

[Blue, black, blue, Grey, green, Pink, purple, Red]

DanielBK
quelle
Eine Bearbeitung für die Lesbarkeit von vorgenommen Comparator. Aber ja, dies setzt voraus, dass nur das erste Zeichen des Strings verglichen wird, auf das das OP auch nicht viel Wert gelegt hat.
Naman
8

Mit RuleBasedCollator können Sie Ihre eigenen Regeln definieren.

Beispiel für eine benutzerdefinierte Regel:

String rules = "< c,C < b,B";

Die obige Regel wird dekodiert, da beim Vergleichen von Zeichenfolgen Csowohl Groß- als auch Kleinbuchstaben vor Groß- und Kleinbuchstaben Bangezeigt werden sollen.

String customRules = "<A<a<B<b<C<c<D<d<E<e<F<f<G<g<H<h<I<i<J<j<K<k<L<l<M<m<N<n<O<o<P<p<Q<q<R<r<S<s<T<t<U<u<V<v<X<x<Y<y<Z<z";
RuleBasedCollator myRuleBasedCollator = new RuleBasedCollator(customRules);
Collections.sort(listOfColors,myRuleBasedCollator);
System.out.println(listOfColors);

Ausgabe:

[Blue, black, blue, Grey, green, Pink, purple, Red]

Bearbeiten: Anstatt das customRulesvon Hand zu schreiben , können Sie es mit dem folgenden Code generieren.

String a = IntStream.range('a', 'z' + 1).mapToObj(c -> Character.toString((char) c))
        .flatMap(ch -> Stream
            .of("<", ch.toUpperCase(), "<", ch)).collect(Collectors.joining(""));
Vishwa Ratna
quelle
2
Schöpfung String customRuleskann Automatismus sein mit IntStream:IntStream.range('a', 'z' + 1) .mapToObj(Character::toString) .flatMap(ch -> Stream.of("<", ch.toUpperCase(), "<", ch)) .collect(Collectors.joining(""))
lczapski
@lczapski, irgendwie .mapToObj(Character::toString)wird nicht gelöst, ich denke, Sie müssen verwenden.mapToObj(c -> Character.toString((char) c))
Vishwa Ratna
mapToObj(Character::toString)funktioniert nur in Java 11 oder neuer.
Holger
@Holger Ok, ich habe mein System (JDK-8) ausprobiert und mapToObj(Character::toString)wurde nicht gelöst, aber ich mochte die Idee, also mache ich am Ende Casting wie.mapToObj(c -> Character.toString((char) c))
Vishwa Ratna
3
Ich würde es vorziehenIntStream.rangeClosed('a', 'z').flatMap(c -> IntStream.of(c,Character.toUpperCase(c))) .mapToObj(c -> Character.toString((char)c)) .collect(Collectors.joining("<", "<", ""));
Holger
0

Sie benötigen eine Methode, die zuerst einen Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung für jeden Buchstaben durchführt. Wenn dann eine Übereinstimmung vorliegt, führen Sie für jeden Buchstaben einen Vergleich mit Groß- und Kleinschreibung durch:

public static int compare(String s1, String s2)
{
    int len, i;
    if (s1.length()<s2.length()) {
        len = s1.length();
    } else {
        len = s2.length();
    }
    for (i=0;i<len;i++) {
        if (Character.toUpperCase(s1.charAt(i)) < Character.toUpperCase(s2.charAt(i))) {
            return -1;
        } else if (Character.toUpperCase(s1.charAt(i)) > Character.toUpperCase(s2.charAt(i))) {
            return 1;
        } else if (s1.charAt(i) < s2.charAt(i)) {
            return -1;
        } else if (s1.charAt(i) > s2.charAt(i)) {
            return 1;
        }
    }
    if (s1.length() < s2.length()) {
        return -1;
    } else if (s1.length() > s2.length()) {
        return 1;
    } else {
        return 0;
    }
}

Sie können diese Methode dann an übergeben Stream.sorted.

dbush
quelle