我有一个单词流,我想根据相同元素(=单词)的出现对它们进行排序。
例如:{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;每个元素都要检查是否相等。
谢谢
答案 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 = -2
和x = 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())));