如何使用 Java Streams 仅收集最大长度的元素?

Fur*_*ish 9 java java-stream

我正在尝试使用 Java StreamsString从我的列表中收集最大长度的所有s:

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());
Run Code Online (Sandbox Code Playgroud)

我希望 mylongest包含{"long word", "long wwww", "llll wwww"},因为那些是String具有最大长度的s。如果只有String最大长度的s之一,我显然希望结果List只包含该元素。

我试图首先对它们进行排序,以便在第一个元素中出现最大长度,但我无法检索流中第一个元素的长度。我可以尝试类似的东西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);
}
Run Code Online (Sandbox Code Playgroud)

但它……吗?我不喜欢引入 dummy wrapper(谢谢,实际上是最终要求)或peek() hack

有没有更优雅的方法来实现这一目标?

Ily*_*nko 8

尝试这个:

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);
Run Code Online (Sandbox Code Playgroud)

输出:

[long word, long wwww, llll wwww]
Run Code Online (Sandbox Code Playgroud)


use*_*296 7

好吧,我不知道这是否会更优雅,但它应该做你想做的:

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);
Run Code Online (Sandbox Code Playgroud)

输出:

[长词,长 wwww,llll wwww]


Lou*_*man 6

您可能认为它更丑陋,但自定义收集器绝对正确、更高效,甚至可以很好地并行化:

Collector<String, List<String>, List<String>> collector = Collector.of(
   ArrayList::new,
   (list, elem) -> {
     if (list.isEmpty() || elem.length() == list.get(0).length()) {
       list.add(elem);
     } else if (elem.length() > list.get(0).length()) {
       list.clear();
       list.add(elem);
     }
   },
   (list1, list2) -> {
     int len1 = list1.isEmpty() ? -1 : list1.get(0).length();
     int len2 = list2.isEmpty() ? -1 : list2.get(0).length();
     if (len1 < len2) {
       return list2;
     } else if (len1 > len2) {
       return list1;
     } else {
       list1.addAll(list2);
       return list1;
     }
   });

return strings.stream().collect(collector);
Run Code Online (Sandbox Code Playgroud)