Java Streams | groupingBy相同的元素

Skl*_*ogW 6 java java-8 java-stream

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

例如:{hello,world,hello}

Map<String, List<String>>
Run Code Online (Sandbox Code Playgroud)

你好你好你好}

世界,{世界}

到目前为止我有什么:

Map<Object, List<String>> list = streamofWords.collect(Collectors.groupingBy(???));
Run Code Online (Sandbox Code Playgroud)

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

问题2:我不知道在胃肠道内放入什么,以同样的方式将其分组.我知道我能够处理lambda表达式中的单个元素,但我不知道如何到达每个元素的"外部"以检查是否相等.

谢谢

Ale*_* C. 8

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

Map<String, List<String>> occurrences = 
     streamOfWords.collect(groupingBy(str -> str));
Run Code Online (Sandbox Code Playgroud)

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

Map<String, Long> occurrences = 
     streamOfWords.collect(groupingBy(str -> str, counting()));
Run Code Online (Sandbox Code Playgroud)

基本上不是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"
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.

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

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

同样,我们有:

f("hello") = "he"
f("world") = "wo"
f("hey") = "he"
Run Code Online (Sandbox Code Playgroud)

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

"he" -> ["hello", "hey"]
"wo" -> ["world"]
Run Code Online (Sandbox Code Playgroud)

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

查看源代码不会帮助您了解最初发生的情况.如果你想知道它是如何在幕后实现的话,它会很有用.但是首先尝试用更高级别的抽象来思考这个概念,然后事情会变得更加清晰.

希望能帮助到你!:)

  • @SklogW你遇到哪些具体问题?源代码不是很容易理解.对于高级图片,"groupingBy"是一个收集器,它在行进中遍历管道时在"Map <K,List <V>"中累积元素.请记住,`str - > str`是一个函数,所以基本上不是做`map.put(str,someValue)`你会做`map.put(function.apply(str),someValue)`.如果您有两个相同的键,则合并List中的值,依此类推.保留类型信息是因为`Function`是一个通用接口,因此您知道应用它后会得到哪种类型. (2认同)
  • @SklogW我想如果我做一个数学类比,你会更好理解.对于任何x,identity函数是`f(x)= x`.在我们的例子中`f(1)!= f(2)`.这同样适用于此.当您的身份函数应用于"hello"时,它返回"hello",当它应用于"world"时,它返回"world".因此`f("hello")!= f("world")`因此根据您提供的功能,您知道它的键不一样. (2认同)
  • @SklogW例如尝试使用函数`str - > str.substring(0,2)`.当收集单词"hey"和"hello"时,我们有`f("嘿")="他"`和`f("你好")="他"`.因此,根据它,在收集元素时,键"he"应该用值"hello"和"hey"映射,因为将函数应用于这些值会产生相同的结果.你会注意到你在这里用前两个字母对元素进行分组. (2认同)
  • @SklogW老实说,我不认为第一次看实施会有所帮助.尝试用更高级别的抽象思考,也许事情会变得更加清晰. (2认同)
  • 如果您仍想查看,请参阅以下相关部分:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/stream/Collectors .java #Collectors.groupingBy%28java.util.function.Function%2Cjava.util.function.Supplier%2Cjava.util.stream.Collector%29 and http://grepcode.com/file/repository.grepcode.com/java /root/jdk/openjdk/8-b132/java/util/stream/ReferencePipeline.java#ReferencePipeline.collect%28java.util.stream.Collector%29 (2认同)

flo*_*flo 7

您要搜索的KeyExtractor是标识功能:

Map<String, List<String>> list = streamofWords.collect(Collectors.groupingBy(Function.identity()));
Run Code Online (Sandbox Code Playgroud)

编辑补充说明:

  • Function.identity() 用一个方法返回一个'Function',它只返回它得到的参数.
  • Collectors.groupingBy(Function<S, K> keyExtractor)提供了一个收集器,它将流的所有元素收集到一个Map<K, List<S>>.它使用keyExtractor实现来检查流的类型对象,SK从中推导出类型的键.此键是映射的键,用于获取(或创建)添加了流元素的结果映射中的列表.

  • @SklogW补充说明.现在更清楚了吗? (2认同)
  • Function.identity()只不过是`return s - > s;`.这只是它返回的lambda的名称,因为它读得更好.是的,这足以达到您的目的.`s - > s.equals(s)`会给你一个Map <Boolean,List <String >>,列表中的所有元素都是'true`(因为所有的字符串都等于它们自己).会有点不确定,是的;-) (2认同)