1

I have a list with some strings in this format:

List<String> ids = new ArrayList<>();

ids.add("B-7");
ids.add("B-5");
ids.add("A-3");
ids.add("B-8");
ids.add("B-1");
ids.add("B-6");
ids.add("B-2");
ids.add("B-3");
ids.add("B-10");
ids.add("A-1");
ids.add("B-4");
ids.add("B-9");
ids.add("A-2");

I need to sort it to have this output, (iterating over the list):

A-1
A-2
A-3
B-1
B-2
B-3
B-4
B-5
B-6
B-7
B-8
B-9
B-10

I am using:

List<String> sortedIds = ids.stream().sorted().collect(Collectors.toList());

But instead, my output:

A-1
A-2
A-3
B-1
B-10   -- Error
B-2
B-3
B-4
B-5
B-6
B-7
B-8
B-9
2
  • 1
    This is where the documentation comes in handy. If you look at Stream#sorted(), notice that it says "sorted according to natural order." As you've demonstrated, you don't want natural order, you want to sort it a different way. Java is way ahead of you; it's got an overloaded method which lets you specify a custom Comparator. Commented Apr 9, 2021 at 22:41
  • Sort on a string that may contain a number Commented Apr 9, 2021 at 22:43

5 Answers 5

5

You can create a custom Comparator using Comparator.comparing and Comparator.thenComparing.

List<String> sortedIds = ids.stream().sorted(
    Comparator.comparing((String s) -> s.substring(0, s.indexOf('-')))
    .thenComparingInt(s -> Integer.parseInt(s.substring(s.indexOf('-') + 1))))
    .collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

1 Comment

By the way, that last line can be .toList() in Java 16 and later.
4

The default Comparator is going to operate in lexicographical order. You need to compare the String parts and Integer parts separately. Something like

Collections.sort(ids, (a, b) -> {
    String[] at = a.split("-");
    String[] bt = b.split("-");
    int c = at[0].compareTo(bt[0]);
    if (c != 0) {
        return c;
    }
    return Integer.valueOf(Integer.parseInt(at[1])).compareTo(Integer.parseInt(bt[1]));
});

Comments

3

What you'll need is a customer Comparator<String> to use inside of the sorted() intermediate operation

List<String> sorted = ids.stream().sorted((o1, o2) -> {
    String[] first = o1.split("-");
    String[] second = o2.split("-");

    int lettersComparison = first[0].compareTo(second[0]);
    if (lettersComparison != 0) {
        return lettersComparison;
    }

    Integer firstNumber = Integer.valueOf(first[1]);
    Integer secondNumber = Integer.valueOf(second[1]);
    return firstNumber.compareTo(secondNumber);
}).toList();

Which outputs

[A-1, A-2, A-3, B-1, B-2, B-3, B-4, B-5, B-6, B-7, B-8, B-9, B-10]

That said, if you want to just sort the existing List, I would suggest not to go through a Stream for that because it has an overhead in terms of performance and creation of new object.

You can use list.sort(comparator) and use the same Comparator<String> as hereabove

ids.sort((o1, o2) -> {
    String[] first = o1.split("-");
    String[] second = o2.split("-");

    int lettersComparison = first[0].compareTo(second[0]);
    if (lettersComparison != 0) {
        return lettersComparison;
    }

    Integer firstNumber = Integer.valueOf(first[1]);
    Integer secondNumber = Integer.valueOf(second[1]);
    return firstNumber.compareTo(secondNumber);
});

Comments

0

Try this.

List<String> ids = Arrays.asList(
    "B-7", "B-5", "A-3", "B-8", "B-1", "B-6", "B-2",
    "B-3", "B-10", "A-1", "B-4", "B-9", "A-2");

Collections.sort(ids,
    Comparator.comparingInt(String::length)
    .thenComparing(Function.identity()));

System.out.println(ids);

output

[A-1, A-2, A-3, B-1, B-2, B-3, B-4, B-5, B-6, B-7, B-8, B-9, B-10]

1 Comment

Very smart ! But what if B-05 ?
0

Here is yet another way. I tend to split first and then glue together after sorting rather than continually splitting inside a comparator.

List<String> result = ids.stream().map(s -> s.split("-"))
        .sorted(Comparator.comparing((String[] a) -> a[0])
                .thenComparingInt(
                        a -> Integer.parseInt(a[1])))
        .map(a -> String.join(",", a))
        .collect(Collectors.toList());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.