Выборочная сортировка таким образом, что A предшествует a, а B - перед b - PullRequest
11 голосов
/ 31 января 2020

У меня есть список таких цветов:

Розовый, синий, красный, синий, серый, зеленый, фиолетовый, черный ... и т. Д. c

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

Есть некоторые промежуточные операции, такие как фильтрация цветов фруктов, теперь у меня остались отфильтрованные результаты, в которых я хочу отсортировать их по порядку:

Синий, черный, синий, серый, зеленый, розовый, фиолетовый, красный

Я пробовал:

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

Это не работает, как ожидалось.

Вывод следующий:

черный, синий, синий, зеленый, серый, розовый, фиолетовый, красный

Я хочу следующее:

Синий, черный, синий, серый, зеленый, розовый, фиолетовый, красный

Ответы [ 3 ]

8 голосов
/ 31 января 2020

Вы можете использовать RuleBasedCollator для определения своих собственных правил.

Пример custom rule:

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

Выше Правило декодируется так, что при сравнении строк заглавные и строчные буквы C должны появляться перед заглавными и строчными B при *. 1013 *

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

Вывод:

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

Редактировать: Вместо написания customRules от руки, вы можете использовать приведенный ниже код для его генерации.

String a = IntStream.range('a', 'z' + 1).mapToObj(c -> Character.toString((char) c))
        .flatMap(ch -> Stream
            .of("<", ch.toUpperCase(), "<", ch)).collect(Collectors.joining(""));
8 голосов
/ 31 января 2020

Мое решение состоит в том, чтобы использовать сортировку в два этапа с использованием метода Comparator.thenComparing().

Сначала сравните строки только по первому символу, игнорируя регистр. Таким образом, группы с одинаковым первым символом (независимо от того, в каком случае) пока остаются несортированными. Затем на втором шаге примените обычную сортировку по алфавиту, чтобы отсортировать эти несортированные подгруппы.

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

Возможно, она все еще может быть оптимизирована, но дает желаемый результат:

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

0 голосов
/ 31 января 2020

Вам нужен метод, который сначала выполняет сравнение без учета регистра для каждой буквы, затем, если есть совпадение, выполняет сравнение с учетом регистра для каждой буквы:

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

Затем вы можете передать этот метод в Stream.sorted.

...