3

我有一个单词列表,说

List<String> words = Arrays.asList("Hello alan i am here where are you"+  
  "and what are you doing hello are you there");

如何按降序获取列表中重复多次的前七个单词?然后单词应按字母顺序排列。所以上面的输出应该是前七个词

you (3)
are (2)
hello (2)
alan (1)
am (1)
and (1)
doing (1)

我正在使用流 lamda 在 Java 8 中执行此操作。

我正在尝试这种方式。首先对列表进行排序其次获取单词映射及其在单词列表中的单词数。

List<String> sortedWords = Arrays.asList("Hello alan i am here where are you and what are you doing hello you there".split(" "))
            .stream().sorted().collect(toList());

Map<String, Long> collect = 
            sortedWords.stream().collect(groupingBy(Function.identity(), counting()));
4

5 回答 5

9

最困难的部分是排序。由于您只想保留结果中的前 7 个元素,并且希望按其值对 Map 进行排序,因此我们需要创建一个包含所有结果的 Map,对其进行排序,然后保留 7 个结果。

在下面的代码中,每个单词都是小写的并按它们自己分组,计算出现次数。然后,我们需要对这个映射进行排序,以便在条目上创建一个 Stream,根据值(按降序)对它们进行排序,然后根据键对其进行排序。保留前 7 个元素,映射到它们的键(对应于单词)并收集到 aList中,从而保持遇到顺序。

public static void main(String[] args) {
    String sentence = "Hello alan i am here where are you and what are you doing hello are you there";
    List<String> words = Arrays.asList(sentence.split(" "));

    List<String> result = 
            words.stream()
                 .map(String::toLowerCase)
                 .collect(groupingBy(identity(), counting()))
                 .entrySet().stream()
                 .sorted(Map.Entry.<String, Long> comparingByValue(reverseOrder()).thenComparing(Map.Entry.comparingByKey()))
                 .limit(7)
                 .map(Map.Entry::getKey)
                 .collect(toList());

    System.out.println(result);
}

输出:

[are, you, hello, alan, am, and, doing]

请注意,您在想要的输出中犯了一个错误:"are"实际上出现了 3 次"you",应该是之前

注意:这段代码假设了很多静态导入,即:

import static java.util.Comparator.reverseOrder;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
于 2015-11-22T14:05:09.167 回答
4

虽然@Tunaki 解决方案很棒,但有趣的是使用我的 StreamEx 库,可以在单个 Stream 管道中解决问题(在调用单个终端操作之前不会执行实际操作):

Map<String, Long> map = StreamEx.of(words)
    .map(String::toLowerCase)
    .sorted() // sort original words, so now repeating words are next to each other
    .runLengths() // StreamEx feature: squash repeating words into Entry<String, Long>
    .sorted(Entry.<String, Long> comparingByValue().reversed()
                 .thenComparing(Entry.comparingByKey()))
    .limit(7) // Sort and limit
    .toCustomMap(LinkedHashMap::new); // Single terminal operation: store to LinkedHashMap

或者如果只需要单词:

List<String> list =StreamEx.of(words)
    .map(String::toLowerCase)
    .sorted() // sort original words, so now repeating words are next to each other
    .runLengths() // StreamEx feature: squash repeating words into Entry<String, Long>
    .sorted(Entry.<String, Long> comparingByValue().reversed()
                 .thenComparing(Entry.comparingByKey()))
    .limit(7) // Sort and limit
    .keys() // Drop counts leaving only words
    .toList(); // Single terminal operation: store to List
于 2015-11-22T14:33:21.593 回答
2

有时,问题的最佳解决方案不是算法,而是数据结构。我想你需要的是一个包。由于您希望输出按出现次数然后按键排序,因此您应该使用的特定数据结构是TreeBag. 以下代码将使用Eclipse Collections和 Java 8 Streams:

String string =
    "Hello alan i am here where are you and what are you doing hello are you there";
List<ObjectIntPair<String>> pairs =
    Stream.of(string.toLowerCase().split(" "))
        .collect(Collectors.toCollection(TreeBag::new))
        .topOccurrences(7);
System.out.println(pairs);

此代码将输出:

// Strings with occurrences
[are:3, you:3, hello:2, alan:1, am:1, and:1, doing:1, here:1, i:1, there:1, what:1, where:1]

topOccurrences()方法具有处理关系的逻辑,这基本上让开发人员决定他们想要如何处理关系的情况。如果您想要此列表中的前七个项目,则可以将调用链接到.take(7);

代码还可以进一步简化为:

List<ObjectIntPair<String>> pairs =
    TreeBag.newBagWith(string.split(" ")).topOccurrences(7);
System.out.println(pairs);

静态工厂方法TreeBag.newBagWith()接受可变参数,因此您可以String.split()直接将结果传递给它。

注意:我是 Eclipse Collections 的提交者。

于 2015-11-24T04:23:36.270 回答
1

我是一个简单的人,所以我会Map<String, Integer>先用 a 来计算每个单词。TreeSet然后为每个计数创建一个,并将它们存储在一个TreeMap<Integer, TreeSet>. 从那里应该相当简单。

于 2015-11-22T13:55:55.120 回答
0

两步解决方案:group/count,然后按 count 降序处理

List<String> words = Arrays.asList("Hello alan i am here where are you and what are you doing hello you there".split(" "));

Map<String, Long> collect = words.stream()
        .map(String::toLowerCase) // convert to lower case
        .collect( // group and count by name
                Collectors.groupingBy(Function.identity(), Collectors.counting()));

collect.keySet().stream()
        .sorted( // order by count descending, then by name
                Comparator
                        .comparing(collect::get)
                        .reversed()
                        .thenComparing(Collator.getInstance()))
        .map(k -> k + " (" + collect.get(k) + ")") // map to name and count string
        .limit(7) // only first 7 entries
        .forEach(System.out::println); // output
于 2016-10-06T11:56:19.457 回答