显示有限流

时间:2017-09-21 10:31:49

标签: java java-8 java-stream

我必须使用Streams API从给定文件中找到所有最长的单词。我在几个步骤中完成了它,但是找了一些"一个衬里",实际上我处理整个文件两次,首先找到单词的最大长度,然后用于比较所有到最大长度,假设它不是最​​好的表现; P有人能帮帮我吗?看看代码:

public class Test {
    public static void main(String[] args) throws IOException {
        List<String> words = Files.readAllLines(Paths.get("alice.txt"));
        OptionalInt longestWordLength = words.stream().mapToInt(String::length).max();
        Map<Integer, List<String>> groupedByLength = words.stream().collect(Collectors.groupingBy(String::length));
        List<String> result = groupedByLength.get(longestWordLength.getAsInt());
    }
}

我希望直截了当:

List<String> words = Files.readAllLines(Paths.get("alice.txt"));
List<String> result = // code

文件每行只包含一个单词,无论如何它并不重要 - 问题是关于正确的流代码。

3 个答案:

答案 0 :(得分:6)

不是只保留最大长度,而是可以将单词从长度收集到单词中,然后只取最长的单词:

List<String> longestWords =
    Files.lines(Paths.get("alice.txt"))
         .collect(Collectors.groupingBy(String::length))
         .entrySet()
         .stream()
         .sorted(Map.Entry.<Integer, List<String>> comparingByKey().reversed())
         .map(Map.Entry::getValue)
         .findFirst()
         .orElse(null);

修改:
正如Malte Hartwig所指出的那样,在流式地图上使用max会更优雅(也可能更快):

List<String> longestWords =
    Files.lines(Paths.get("alice.txt"))
         .collect(Collectors.groupingBy(String::length))
         .entrySet()
         .stream()
         .max(Map.Entry.comparingByKey())
         .map(Map.Entry::getValue)
         .orElse(null);

<强> EDIT2:
在上述两种解决方案中都存在内置的低效率,因为它们都构建了一个映射,基本上存储了文件中全部字符串的长度而不是最长的字符串。如果性能比使用中的优雅更重要,您可以编写自己的Collector来保留列表中最长的字符串:

private static int stringInListLength(List<String> list) {
    return list.stream().map(String::length).findFirst().orElse(0);
}

List<String> longestWords =
    Files.lines(Paths.get("alice.txt"))
         .collect(Collector.of(
             LinkedList::new,
             (List<String> list, String string) -> {
                 int stringLen = string.length();
                 int listStringLen = stringInListLength(list);
                 if (stringLen > listStringLen) {
                     list.clear();
                 }
                 if (stringLen >= listStringLen) {
                     list.add(string);
                 }
             },
             (list1, list2) -> {
                 int list1StringLen = stringInListLength(list1);
                 int list2StringLen = stringInListLength(list2);
                 if (list1StringLen > list2StringLen) {
                     return list1;
                 }
                 if (list2StringLen > list1StringLen) {
                     return list2;
                 }
                 list1.addAll(list2);
                 return list1;
             }
         ));

答案 1 :(得分:3)

reduce会帮助您:

    Optional<String> longest = words.stream()
            .reduce((s1, s2) -> {
                if (s1.length() > s2.length())
                    return s1;
                else
                    return s2;
            });

如果Stream为空,则会返回Optional.empty

如果您想要所有具有最大长度的单词列表,这件作品将对您有所帮助:

    Optional<List<String>> longest = words.stream()
            .collect(Collectors.groupingBy(
                    String::length,
                    Collectors.toList()
            ))
            .entrySet()
            .stream()
            .reduce(
                    (entry1, entry2) -> {
                        if (entry1.getKey() > entry2.getKey())
                            return entry1;
                        else
                            return entry2;
                    }
            )
            .map(Map.Entry::getValue);

答案 2 :(得分:0)

遍历地图键以找到最长的字长