Custom Sorting in way that A comes before a and B comes before b

My solution is to use sorting in two steps by using the Comparator.thenComparing() method.

First, compare the Strings only by the first character ignoring case. So the groups with the same first character (no matter what case) remain unsorted so far. Then in the second step apply the normal alphabetical sorting to sort those unsorted subgroups.

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

Maybe it can be still optimized, but it gives the desired result:

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


You can use RuleBasedCollator to define your own Rules.

Example of custom rule:

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

The above rule is decoded as that both uppercase and lowercase C's are to appear before both uppercase and lowercase B's when comparing strings.

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

Output:

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

Edit: Instead of writing the customRules by hand, you can use the below to code to generate it.

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