Спеціальне сортування таким чином, що А надходить до a, а B - перед b


11

У мене є такий список кольорів:

Рожевий, синій, червоний, синій, сірий, зелений, фіолетовий, чорний ... тощо

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

Є деякі проміжні операції, такі як фільтрування деяких фруктових кольорів, тепер у мене залишаються відфільтровані результати там, де я хочу їх сортувати за порядком:

Синій, чорний, синій, сірий, зелений, рожевий, фіолетовий, червоний

Я намагався :

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

Це не працює, як очікувалося.

Вихід такий:

чорний, Синій, синій, зелений, Сірий, Рожевий, фіолетовий, Червоний

Я хочу наступне:

Синій, чорний, синій, сірий, зелений, рожевий, фіолетовий, червоний


2
Чи не повинен чорний колір ставати перед синім, а зелений перед сірим?
Равіндра Ранвала

3
aє раніше, uтому результат правильний
Єнс

2
@RavindraRanwala, синій «s B є столицею , але назад » s б не є.
Vishwa Ratna

2
Я спробував, але це конкретний наказ не дає. Це дає [black, Blue, blue, green, Grey, Pink, purple, Red]@ chrylis-onstrike-
Равіндра Ранвала

2
Якщо ви хочете, щоб капітальний кожух ставав перед нижчим корпусом, то ігнорування корпусу - це останнє, що ви хочете.
Teepeemm

Відповіді:


8

Моє рішення - використовувати сортування в два етапи, використовуючи 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]


Внесли правки для читабельності Comparator. Але так, це передбачає лише порівняння першого персонажа рядка, про який ОП також не особливо наголосив.
Наман

8

Ви можете використовувати RuleBasedCollator для визначення власних Правил.

Приклад спеціального правила:

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

Наведене вище правило розшифровується таким чином, що перед великими та малими літерами Cмають з’являтися і великі, і маліB при порівнянні рядків.

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

2
створення String customRulesможе бути автоматизмом із IntStream:IntStream.range('a', 'z' + 1) .mapToObj(Character::toString) .flatMap(ch -> Stream.of("<", ch.toUpperCase(), "<", ch)) .collect(Collectors.joining(""))
lczapski

@lczapski, якимось чином .mapToObj(Character::toString)не вирішуватимуся, я думаю, вам потрібно скористатися.mapToObj(c -> Character.toString((char) c))
Vishwa Ratna

mapToObj(Character::toString)працює лише в Java 11 або новіших версіях.
Хольгер

@Holger Добре, я спробував у моїй системі (JDK-8) і mapToObj(Character::toString)не вирішувався, але мені сподобалась ідея, тому я в кінцевому підсумку роблю кастинг, як.mapToObj(c -> Character.toString((char) c))
Vishwa Ratna

3
Я вважаю за кращеIntStream.rangeClosed('a', 'z').flatMap(c -> IntStream.of(c,Character.toUpperCase(c))) .mapToObj(c -> Character.toString((char)c)) .collect(Collectors.joining("<", "<", ""));
Хольгер

0

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

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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.