有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我正在尝试使用Java Streams从我的列表中收集所有最长的String

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

我希望我的longest包含{"long word", "long wwww", "llll wwww"},因为这些是长度最大的String。在只有一个长度最大的String的情况下,我显然希望得到的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);
}

但是<丑陋的?我不喜欢引入虚拟包装器(谢谢,实际上是最终的要求)或peek()hack

有没有更优雅的方法来实现这一点


共 (4) 个答案

  1. # 1 楼答案

    我不知道你是否觉得它更优雅,但它很简洁:

           List<String> strings = Arrays.asList("long word", "short", "long wwww", "llll wwww", "shr");
    
           TreeMap<Integer, List<String>> collect = strings.stream().collect(
                    Collectors.groupingBy(
                            String::length,
                            TreeMap::new,
                            mapping(Function.identity(), toList())));
    
           System.out.println(collect.lastEntry().getValue());
    
  2. # 2 楼答案

    嗯,我不知道这是否会更优雅,但它应该满足您的要求:

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

    输出:

    [long word, long wwww, llll wwww]
    
  3. # 3 楼答案

    试试这个:

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

    输出:

    [long word, long wwww, llll wwww]
    
  4. # 4 楼答案

    您可以认为它更丑陋,但是自定义收集器确实是正确的、更有效的,甚至是并行的:

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