Java 8/JDK8 Stream 的函数 Map/Reduce To group List<String> into Map<String, List<String>>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21978653/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8/JDK8 Stream's functions Map/Reduce To group List<String> into Map<String, List<String>>
提问by Marcello de Sales
Java 8 is about to be released... While learning about Streams, I got into a scenario about grouping anagrams using one of the new ways. The problem I'm facing is that I can't find a way to group Strings objects using the map/reduce functions. Instead, I had to create a similar way as documented at http://docs.oracle.com/javase/tutorial/collections/streams/reduction.html.
Java 8 即将发布...在学习 Streams 的过程中,我进入了一个关于使用其中一种新方法对字谜进行分组的场景。我面临的问题是我找不到使用 map/reduce 函数对 Strings 对象进行分组的方法。相反,我必须创建与http://docs.oracle.com/javase/tutorial/collections/streams/reduction.html 中记录的类似的方式。
Based on the documentation, we can simply use LIST.stream().collect(Collectors.groupingBy(POJO::GET_METHOD)), so that Collectors.groupingBy() will aggregate the keys of the map based on the method used. However, this approach is too seems to be cumbersome to wrap a simple String presentation.
根据文档,我们可以简单地使用 LIST.stream().collect(Collectors.groupingBy(POJO::GET_METHOD)),这样 Collectors.groupingBy() 将根据使用的方法聚合地图的键。但是,这种方法对于包装一个简单的 String 表示似乎太麻烦了。
public class AnagramsGrouping {
static class Word {
public String original;
public Word(String word) {
original = word;
}
public String getKey() {
char[] characters = input.toCharArray();
Arrays.sort(characters);
return new String(characters);
}
public String toString() {
return original;
};
}
public static void main(String[] args) {
List<Word> words = Arrays.asList(new Word("pool"), new Word("loop"),
new Word("stream"), new Word("arc"), new Word("odor"),
new Word("car"), new Word("rood"), new Word("meats"),
new Word("fires"), new Word("fries"), new Word("night"),
new Word("thing"), new Word("mates"), new Word("teams"));
Map<String, List<Word>> anagrams = words.stream().collect(
Collectors.groupingBy(Word::getKey));
System.out.println(anagrams);
// This prints the following:
{door=[odor, rood], acr=[arc, car], ghint=[night, thing],
aemrst=[stream], efirs=[fires, fries], loop=[pool, loop],
aemst=[meats, mates, teams]}
Instead, I'm looking for a simpler and more direct solution that uses the new map/reduce functions to accumulate the results into the similar interface Map. Based on https://stackoverflow.com/a/20887747/433814, I have the following:
相反,我正在寻找一种更简单、更直接的解决方案,它使用新的 map/reduce 函数将结果累积到类似的界面 Map 中。基于https://stackoverflow.com/a/20887747/433814,我有以下内容:
List<String> words2 = Arrays.asList("pool", "loop", "stream", "arc",
"odor", "car", "rood", "meats", "fires", "fries",
"night", "thing", "mates", "teams");
words2.stream().collect(Collectors.toMap(w -> sortChars(w), w -> w));
But this code generates a key collision as it is a Map of 1-1. "Exception in thread "main" java.lang.IllegalStateException: Duplicate key pool", which makes sense... Is there a way to group them into the similar output as the first solution with groupingBy, but without using a POJO wrapping the values?
但是这段代码会产生一个键冲突,因为它是一个 1-1 的 Map。“线程“main”中的异常java.lang.IllegalStateException:重复密钥池”,这是有道理的......有没有办法将它们分组到与groupingBy的第一个解决方案类似的输出中,但不使用POJO包装值?
采纳答案by Stuart Marks
The single-argument groupingBy
collector does exactly what you want to do. It classifies its input, which you've already done using sortChars
(or getKey
in the earlier example). Each stream value that's classified under the same key gets put into a list which is the map's value. Thus we have:
单参数groupingBy
收集器完全符合您的要求。它对其输入进行分类,您已经使用sortChars
(或getKey
在前面的示例中)进行了分类。分类在同一键下的每个流值都会放入一个列表中,该列表是映射的值。因此我们有:
Map<String, List<String>> anagrams =
words2.stream().collect(Collectors.groupingBy(w -> sortChars(w)));
giving the output
给出输出
{door=[odor, rood], acr=[arc, car], ghint=[night, thing], aemrst=[stream],
efirs=[fires, fries], loop=[pool, loop], aemst=[meats, mates, teams]}
You could also use a method reference:
您还可以使用方法参考:
Map<String, List<String>> anagrams =
words2.stream().collect(Collectors.groupingBy(GroupingAnagrams::sortChars));
If you want to do something with the values other than building up a list, use a multi-arg overload of groupingBy
and a "downstream" collector. For example, to count the words instead of building up a list, do this:
如果你想对这些值做一些事情而不是建立一个列表,请使用一个多参数重载groupingBy
和一个“下游”收集器。例如,要计算单词而不是建立列表,请执行以下操作:
Map<String, Long> anagrams =
words2.stream().collect(
Collectors.groupingBy(GroupingAnagrams::sortChars, Collectors.counting()));
This results in:
这导致:
{door=2, acr=2, ghint=2, aemrst=1, efirs=2, loop=2, aemst=3}
EDIT:
编辑:
In case it wasn't clear, sortChars
is simply a static function that performs a similar function to what getKey
did in the first example, but from string to string:
如果不清楚,sortChars
它只是一个静态函数,它执行与getKey
第一个示例中所做的类似的功能,但从字符串到字符串:
public static String sortChars(String input) {
char[] characters = input.toCharArray();
Arrays.sort(characters);
return new String(characters);
}