Java Streams | groupingBy相同的元素

时间:2015-04-29 14:38:40

标签: java java-8 java-stream

我有一个单词流,我想根据相同元素(=单词)的出现对它们进行排序。

例如:{hello,world,hello}

Map<String, List<String>>

你好,{你好,你好}

世界,{world}

到目前为止:

Map<Object, List<String>> list = streamofWords.collect(Collectors.groupingBy(???));

问题1:流似乎丢失了他正在处理字符串的信息,因此编译器强制我将类型更改为Object,List

问题2:我不知道在胃肠道内放入什么内容以便将其分组。我知道我能够处理lambda-expression中的单个元素,但我不知道如何到达&#34; outside&#34;每个元素都要检查是否相等。

谢谢

2 个答案:

答案 0 :(得分:8)

要获得Map<String, List<String>>,您只需要告诉groupingBy收集器您要按身份对值进行分组,因此函数x -> x

Map<String, List<String>> occurrences = 
     streamOfWords.collect(groupingBy(str -> str));

然而,这有点无用,因为你看到你有两次相同类型的信息。您应该查看Map<String, Long>,其中值表示流中String的出现。

Map<String, Long> occurrences = 
     streamOfWords.collect(groupingBy(str -> str, counting()));

基本上,不是让groupingBy返回值为List,而是使用下游收集器counting()来告诉您要计算此值出现的次数。

您的排序要求应该意味着您应该有一个Map<Long, List<String>>(如果不同的字符串出现的次数相同怎么样?),并且默认的toMap收集器返回HashMap,它没有订购的概念,但您可以将元素存储在TreeMap中。

我试图总结一下我在评论中所说的内容。

str -> str如何判断“你好”或“世界”是不同的,你似乎遇到了麻烦。

首先str -> str是一个函数,也就是说,对于输入x,产生一个值f(x)。例如,f(x) = x + 2是一个函数,任何值x都会返回x + 2

这里我们使用的是身份功能,即f(x) = x。当您从Map中的管道收集元素时,将在调用此函数之前从该值获取键。所以在你的例子中,你有3个身份函数产生的元素:

f("hello") = "hello"
f("world") = "world"

到目前为止一切顺利。

现在调用collect()时,对于流中的每个值,您将在其上应用函数并评估结果(这将是Map中的键)。如果一个键已经存在,我们将获取当前映射的值,并将List合并到我们想要放置的值(即刚刚应用该函数的值)与之前的映射值。这就是你最后获得Map<String, List<String>>的原因。

让我们再看一个例子。现在,流包含值“hello”,“world”和“hey”,我们要应用于对元素进行分组的函数是str -> str.substring(0, 2),即获取字符串前两个字符的函数。

同样,我们有:

f("hello") = "he"
f("world") = "wo"
f("hey") = "he"

在这里你看到“hello”和“hey”在应用函数时产生相同的密钥,因此在收集它们时它们将被分组在同一个List中,因此最终的结果是:

"he" -> ["hello", "hey"]
"wo" -> ["world"]

要与数学进行类比,你可以采用任何非双射函数,例如x 2 。对于x = -2x = 2,我们有f(x) = 4。因此,如果我们通过此函数对整数进行分组,则-2和2将位于相同的“包”中。

查看源代码不会帮助您了解最初发生的事情。如果你想知道它是如何在幕后实现的话,它会很有用。但首先要考虑更高抽象层次的概念,然后事情会变得更加清晰。

希望它有所帮助! :)

答案 1 :(得分:0)

如果要按对象的某些字段(而不是整个对象)分组,并且不想更改equals和hashCode方法,则我将创建一个类,其中包含一组用于分组的键:

import java.util.Arrays;

@Getter
public class MultiKey {

    public MultiKey(Object... keys) {
        this.keys = keys;
    }

    private Object[] keys;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MultiKey multiKey = (MultiKey) o;
        return Arrays.equals(keys, multiKey.keys);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(keys);
    }

}

还有groupingBy本身:

Map<MultiKey, List<VhfEventView>> groupedList = list
        .stream()
        .collect(Collectors.groupingBy(
                 e -> new MultiKey(e.getGroupingKey1(), e.getGroupingKey2())));