Java 展平集合
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18290935/
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
Flattening a collection
提问by Tony Ennis
Say I have a Map<? extends Object, List<String>>
说我有一个 Map<? extends Object, List<String>>
I can get the values of the map easily enough, and iterate over it to produce a single List<String>
.
我可以很容易地获取地图的值,并对其进行迭代以生成单个List<String>
.
for (List<String> list : someMap.values()) {
someList.addAll(list);
}
Is there a way to flatten it in one shot?
有没有办法一口气把它弄平?
List<String> someList = SomeMap.values().flatten();
采纳答案by Josh M
If you are using Java 8, you could do something like this:
如果您使用的是 Java 8,则可以执行以下操作:
someMap.values().forEach(someList::addAll);
回答by Joni
No, there is no shorter method. You have to use a loop.
不,没有更短的方法。你必须使用一个循环。
Update Apr 2014:Java 8 has finally come out. In the new version you can use the Iterable.forEach
method to walk over a collection without using an explicit loop.
2014 年 4 月更新:Java 8 终于出来了。在新版本中,您可以使用该Iterable.forEach
方法遍历集合,而无需使用显式循环。
Update Nov 2017:Found this question by chance when looking for a modern solution. Ended up going with reduce
:
2017 年 11 月更新:在寻找现代解决方案时偶然发现了这个问题。结束了reduce
:
someMap.values().stream().reduce(new ArrayList(), (accum, list) -> {
accum.addAll(list);
return accum;
}):
This avoids depending on mutable external state of forEach(someList::addAll)
the overhead of flatMap(List::stream)
.
这避免了依赖于forEach(someList::addAll)
的开销的可变外部状态flatMap(List::stream)
。
回答by David
If you just want to iterate through values, you can avoid all these addAll methods.
如果您只想遍历值,则可以避免所有这些 addAll 方法。
All you have to do is write a class that encapsulates your Map, and that implements the Iterator :
您所要做的就是编写一个封装 Map 并实现 Iterator 的类:
public class ListMap<K,V> implements Iterator<V>
{
private final Map<K,List<V>> _map;
private Iterator<Map.Entry<K,List<V>>> _it1 = null;
private Iterator<V> _it2 = null;
public ListMap(Map<K,List<V>> map)
{
_map = map;
_it1 = map.entrySet().iterator();
nextList();
}
public boolean hasNext()
{
return _it2!=null && _it2.hasNext();
}
public V next()
{
if(_it2!=null && _it2.hasNext())
{
return _it2.next();
}
else
{
throw new NoSuchElementException();
}
nextList();
}
public void remove()
{
throw new NotImplementedException();
}
private void nextList()
{
while(_it1.hasNext() && !_it2.hasNext())
{
_it2 = _it1.next().value();
}
}
}
回答by Craig P. Motlin
If you're using Eclipse Collections, you can use Iterate.flatten().
如果您使用Eclipse Collections,则可以使用Iterate.flatten()。
MutableMap<String, MutableList<String>> map = Maps.mutable.empty();
map.put("Even", Lists.mutable.with("0", "2", "4"));
map.put("Odd", Lists.mutable.with("1", "3", "5"));
MutableList<String> flattened = Iterate.flatten(map, Lists.mutable.empty());
Assert.assertEquals(
Lists.immutable.with("0", "1", "2", "3", "4", "5"),
flattened.toSortedList());
flatten()
is a special case of the more general RichIterable.flatCollect().
flatten()
是更通用的RichIterable.flatCollect() 的特例。
MutableList<String> flattened =
map.flatCollect(x -> x, Lists.mutable.empty());
Note: I am a committer for Eclipse Collections.
注意:我是 Eclipse Collections 的提交者。
回答by wumpz
Using Java 8 and if you prefer not to instantiate a List
instance by yourself, like in the suggested (and accepted) solution
使用 Java 8,如果您不想自己实例化List
实例,就像在建议(和接受)的解决方案中一样
someMap.values().forEach(someList::addAll);
You could do it all by streaming with this statement:
您可以通过使用以下语句进行流式传输来完成所有操作:
List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());
By the way it should be interesting to know, that on Java 8 the accepted version seems to be indeed the fastest. It has about the same timing as a
顺便说一下,知道在 Java 8 上接受的版本似乎确实是最快的应该很有趣。它的时间与
for (List<String> item : someMap.values()) ...
and is a way faster than the pure streaming solution. Here is my little testcode. I explicitly don't name it benchmark to avoid the resulting discussion of benchmark flaws. ;) I do every test twice to hopefully get a full compiled version.
并且比纯流媒体解决方案快得多。这是我的小测试代码。我明确没有将其命名为基准,以避免由此产生的对基准缺陷的讨论。;) 我每次测试都做两次,希望能得到一个完整的编译版本。
Map<String, List<String>> map = new HashMap<>();
long millis;
map.put("test", Arrays.asList("1", "2", "3", "4"));
map.put("test2", Arrays.asList("10", "20", "30", "40"));
map.put("test3", Arrays.asList("100", "200", "300", "400"));
int maxcounter = 1000000;
System.out.println("1 stream flatmap");
millis = System.currentTimeMillis();
for (int i = 0; i < maxcounter; i++) {
List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());
}
System.out.println(System.currentTimeMillis() - millis);
System.out.println("1 parallel stream flatmap");
millis = System.currentTimeMillis();
for (int i = 0; i < maxcounter; i++) {
List<String> someList = map.values().parallelStream().flatMap(c -> c.stream()).collect(Collectors.toList());
}
System.out.println(System.currentTimeMillis() - millis);
System.out.println("1 foreach");
millis = System.currentTimeMillis();
for (int i = 0; i < maxcounter; i++) {
List<String> mylist = new ArrayList<String>();
map.values().forEach(mylist::addAll);
}
System.out.println(System.currentTimeMillis() - millis);
System.out.println("1 for");
millis = System.currentTimeMillis();
for (int i = 0; i < maxcounter; i++) {
List<String> mylist = new ArrayList<String>();
for (List<String> item : map.values()) {
mylist.addAll(item);
}
}
System.out.println(System.currentTimeMillis() - millis);
System.out.println("2 stream flatmap");
millis = System.currentTimeMillis();
for (int i = 0; i < maxcounter; i++) {
List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());
}
System.out.println(System.currentTimeMillis() - millis);
System.out.println("2 parallel stream flatmap");
millis = System.currentTimeMillis();
for (int i = 0; i < maxcounter; i++) {
List<String> someList = map.values().parallelStream().flatMap(c -> c.stream()).collect(Collectors.toList());
}
System.out.println(System.currentTimeMillis() - millis);
System.out.println("2 foreach");
millis = System.currentTimeMillis();
for (int i = 0; i < maxcounter; i++) {
List<String> mylist = new ArrayList<String>();
map.values().forEach(mylist::addAll);
}
System.out.println(System.currentTimeMillis() - millis);
System.out.println("2 for");
millis = System.currentTimeMillis();
for (int i = 0; i < maxcounter; i++) {
List<String> mylist = new ArrayList<String>();
for (List<String> item : map.values()) {
mylist.addAll(item);
}
}
System.out.println(System.currentTimeMillis() - millis);
And here are the results:
结果如下:
1 stream flatmap
468
1 parallel stream flatmap
1529
1 foreach
140
1 for
172
2 stream flatmap
296
2 parallel stream flatmap
1482
2 foreach
156
2 for
141
Edit 2016-05-24 (two years after):
编辑 2016-05-24(两年后):
Running the same test using an actual Java 8 version (U92) on the same machine:
在同一台机器上使用实际的 Java 8 版本 (U92) 运行相同的测试:
1 stream flatmap
313
1 parallel stream flatmap
3257
1 foreach
109
1 for
141
2 stream flatmap
219
2 parallel stream flatmap
3830
2 foreach
125
2 for
140
It seems that there is a speedup for sequential processing of streams and an even larger overhead for parallel streams.
流的顺序处理似乎有加速,而并行流的开销更大。
Edit 2018-10-18 (four years after):
编辑 2018-10-18(四年后):
Using now Java 10 version (10.0.2) on the same machine:
现在在同一台机器上使用 Java 10 版本 (10.0.2):
1 stream flatmap
393
1 parallel stream flatmap
3683
1 foreach
157
1 for
175
2 stream flatmap
243
2 parallel stream flatmap
5945
2 foreach
128
2 for
187
The overhead for parallel streaming seems to be larger.
并行流的开销似乎更大。
Edit 2020-05-22 (six years after):
编辑 2020-05-22(六年后):
Using now Java 14 version (14.0.0.36) on the same machine:
现在在同一台机器上使用 Java 14 版本 (14.0.0.36):
1 stream flatmap
299
1 parallel stream flatmap
3209
1 foreach
202
1 for
170
2 stream flatmap
178
2 parallel stream flatmap
3270
2 foreach
138
2 for
167
It should be noted, that this was done on a different machine (but I think comparable). The parallel streaming overhead seems to be considerably smaller than before.
应该注意的是,这是在不同的机器上完成的(但我认为具有可比性)。并行流开销似乎比以前小得多。
回答by user2418306
When searching for "java 8 flatten" this is the only mentioning. And it's not about flattening stream either. So for great good I just leave it here
在搜索“java 8 flatten”时,这是唯一提到的。这也不是关于扁平化流。所以为了好我就把它留在这里
.flatMap(Collection::stream)
I'm also surprised no one has given concurrent java 8 answer to original question which is
我也很惊讶没有人对原始问题给出并发 java 8 答案
.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
回答by Guy Grin
A nice solution for the subcase of a Map of Maps is to store, if possible, the data in Guava's Table
.
对于 Map of Maps 的子案例,一个不错的解决方案是,如果可能,将数据存储在 Guava 的Table
.
https://github.com/google/guava/wiki/NewCollectionTypesExplained#table
https://github.com/google/guava/wiki/NewCollectionTypesExplained#table
So for instance a Map<String,Map<String,String>>
is replaced by Table<String,String,String>
which is already flattend. In fact, the docs say that HashBasedTable
, Table
's Hash implementation, is essentially backed by a HashMap<R, HashMap<C, V>>
因此,例如 aMap<String,Map<String,String>>
被替换为Table<String,String,String>
which 已经变平了。事实上,文档说HashBasedTable
,Table
的哈希实现,本质上是由一个HashMap<R, HashMap<C, V>>
回答by orbfish
Suggested by a colleague:
同事推荐:
listOfLists.stream().flatMap(e -> e.stream()).collect(Lists.toList())
I like it better than forEach().
我比 forEach() 更喜欢它。
回答by Leo Duarte
Flatten on a function:
在函数上展平:
private <A, T> List<T> flatten(List<A> list, Function<A, List<T>> flattenFn) {
return list
.stream()
.map(flattenFn)
.flatMap(Collection::stream)
.collect(Collectors.toUnmodifiableList());
}