Ordinamento personalizzato nel modo in cui A viene prima di a e B viene prima di b


11

Ho un elenco di colori come questo:

Rosa, blu, rosso, blu, grigio, verde, viola, nero ... ecc

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

Ci sono alcune operazioni intermedie come il filtraggio di alcuni colori di frutta, ora sono rimasto con risultati filtrati dove voglio che vengano ordinati in ordine:

Blu, nero, blu, grigio, verde, rosa, viola, rosso

Ho provato :

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

Non funziona come previsto.

L'output è il seguente:

nero, blu, blu, verde, grigio, rosa, viola, rosso

Voglio quanto segue:

Blu, nero, blu, grigio, verde, rosa, viola, rosso


2
Il nero non dovrebbe venire prima del blu e del verde prima del grigio?
Ravindra Ranwala,

3
aè prima uquindi il risultato è corretto
Jens

2
@RavindraRanwala, la B di Blue è la capitale ma la b di back non lo è.
Vishwa Ratna,

2
Ho provato, ma non dà questo ordine specifico. Dà [black, Blue, blue, green, Grey, Pink, purple, Red]@ chrylis-onstrike-
Ravindra Ranwala il

2
Se vuoi che la cassa del capitale venga prima della cassa inferiore, ignorare la cassa è l'ultima cosa che desideri.
Teepeemm,

Risposte:


8

La mia soluzione è utilizzare l'ordinamento in due passaggi utilizzando il Comparator.thenComparing()metodo

Innanzitutto, confronta le stringhe solo con il primo carattere ignorando il caso. Quindi i gruppi con lo stesso primo carattere (indipendentemente dal caso) rimangono indifferenti finora. Quindi, nel secondo passaggio, applica il normale ordinamento alfabetico per ordinare quei sottogruppi non ordinati.

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);

Forse può essere ancora ottimizzato, ma dà il risultato desiderato:

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


Ho apportato una modifica per la leggibilità di Comparator. Ma sì, questo presuppone solo il confronto del primo carattere della Stringa su cui neanche l'OP ha enfatizzato molto.
Naman,

8

È possibile utilizzare RuleBasedCollator per definire le proprie regole.

Esempio di regola personalizzata :

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

La regola di cui sopra viene decodificata in quanto sia le lettere maiuscole che quelle minuscole Cdevono comparire prima delle lettere maiuscole e minuscole Bquando si confrontano le stringhe.

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);

Produzione:

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

Modifica: invece di scrivere customRulesa mano, puoi usare il codice seguente per generarlo.

String a = IntStream.range('a', 'z' + 1).mapToObj(c -> Character.toString((char) c))
        .flatMap(ch -> Stream
            .of("<", ch.toUpperCase(), "<", ch)).collect(Collectors.joining(""));

2
la creazione String customRulespuò essere automatismo con IntStream:IntStream.range('a', 'z' + 1) .mapToObj(Character::toString) .flatMap(ch -> Stream.of("<", ch.toUpperCase(), "<", ch)) .collect(Collectors.joining(""))
lczapski il

@lczapski, in qualche modo .mapToObj(Character::toString)non verrà risolto, immagino che devi usare.mapToObj(c -> Character.toString((char) c))
Vishwa Ratna

mapToObj(Character::toString)funziona solo in Java 11 o versioni successive.
Holger

@Holger Ok, ho provato il mio sistema (JDK-8) e mapToObj(Character::toString)non mi sono risolto, ma mi è piaciuta l'idea, quindi finisco per fare casting come.mapToObj(c -> Character.toString((char) c))
Vishwa Ratna,

3
PreferireiIntStream.rangeClosed('a', 'z').flatMap(c -> IntStream.of(c,Character.toUpperCase(c))) .mapToObj(c -> Character.toString((char)c)) .collect(Collectors.joining("<", "<", ""));
Holger il

0

È necessario un metodo che per prima cosa faccia un confronto senza distinzione tra maiuscole e minuscole su ogni lettera, quindi se c'è una corrispondenza esegui un confronto tra maiuscole e minuscole su ogni lettera:

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;
    }
}

È quindi possibile passare questo metodo a Stream.sorted.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.