Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I collect only the elements of the greatest length with Java Streams? [duplicate]

I am trying to use Java Streams to collects all the Strings of the greatest length from my list:

List<String> strings = Arrays.asList("long word", "short", "long wwww", "llll wwww", "shr");

List<String> longest = strings.stream()
            .sorted(Comparator.comparingInt(String::length).reversed())
            .takeWhile(???)
            .collect(Collectors.toList());

I would like my longest to contain {"long word", "long wwww", "llll wwww"}, because those are the Strings that have the greatest lengths. In case of only having one of the Strings with greatest length, I am obviously expecting the resulting List to contain only that element.

I tried to first sort them in order to have the greatest length appear in the first element, but I am unable to retrieve the length of the first element in the stream. I could try something like peek():

static class IntWrapper {
    int value;
}

public static void main(String[] args) throws IOException {
    List<String> strings = Arrays.asList("long word", "short", "long wwww", "llll wwww", "shr");

    IntWrapper wrapper = new IntWrapper();

    List<String> longest = strings.stream()
            .sorted(Comparator.comparingInt(String::length).reversed())
            .peek(s -> {
                if (wrapper.value < s.length()) wrapper.value = s.length();
            })
            .takeWhile(s -> s.length() == wrapper.value)
            .collect(Collectors.toList());

    System.out.println(longest);
}

but it's... ugly? I don't like the introduction of dummy wrapper (thank you, effectively final requirement) or the peek() hack.

Is there any more elegant way to achieve this?

like image 869
Fureeish Avatar asked Mar 19 '20 21:03

Fureeish


2 Answers

Try this:

List<String> strings = Arrays.asList("long word", "short", "long wwww", "llll wwww", "shr");

List<String> longest = strings.stream()
        .collect(groupingBy(String::length, TreeMap::new, toList()))
        .lastEntry()
        .getValue();

System.out.println(longest);

Output:

[long word, long wwww, llll wwww]
like image 177
Ilya Lysenko Avatar answered Sep 26 '22 03:09

Ilya Lysenko


Well, I don't know if this will be more elegant but it should do what you want:

List<String> strings = Arrays.asList("long word", "short", "long wwww", "llll wwww", "shr");

List<String> longest = strings.stream()
        .collect(Collectors.groupingBy(String::length))     // Build Map<Length, List<Strings>>
        .entrySet().stream()                                // EntrySet stream of said map
        .max(Map.Entry.comparingByKey())                    // Keep max length
        .map(Map.Entry::getValue)                           // Get value of max length
        .orElse(Collections.emptyList());                   // Or return an empty list if there's none

System.out.println(longest);

Output:

[long word, long wwww, llll wwww]
like image 29
vicpermir Avatar answered Sep 26 '22 03:09

vicpermir