自定义排序方式是A排在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
黑色难道不应该比蓝色先于蓝色和绿色吗?
Ravindra Ranwala

3
a在此之前,u因此结果是正确的
Jens

2
@RavindraRanwala,蓝色B是大写字母,但后面b不是。
Vishwa Ratna

2
我试过了,但是没有给出这个具体命令。它提供了[black, Blue, blue, green, Grey, Pink, purple, Red]@ chrylis-onstrike-
拉文德拉·兰瓦拉

2
如果要使大写字母优先于小写字母优先,那么忽略大写字母是您最后想要的。
Teepeemm

Answers:


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。但是,是的,这仅假设比较了OP也没有特别强调的String 的第一个字符
纳曼

8

您可以使用RuleBasedCollat​​or定义自己的规则。

自定义规则的示例:

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可以是自动进行的IntStreamIntStream.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好的,我尝试使用我的System(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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.