展平collections

说我有一个Map<? extends Object, List<String>> Map<? extends Object, List<String>>

我可以很容易地获得地图的值,并遍历它来产生一个List<String>

  for (List<String> list : someMap.values()) { someList.addAll(list); } 

有一种方法可以一举把它弄平吗?

  List<String> someList = SomeMap.values().flatten(); 

如果你正在使用Java 8,你可以这样做:

 someMap.values().forEach(someList::addAll); 

使用Java 8,如果您不想自己实例化一个List实例,就像在build议的(并被接受的)解决scheme中一样

 someMap.values().forEach(someList::addAll); 

您可以通过以下声明进行stream式处理:

 List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList()); 

顺便说一下,应该有趣的是,在Java 8上,接受的版本似乎确实是最快的。 它和a的时间差不多

 for (List<String> item : someMap.values()) ... 

并且比纯stream媒体解决scheme更快。 这是我的小testing代码。 我明确地不把它命名为基准,以避免由此导致的基准缺陷的讨论。 ;)我做了两次testing,希望得到一个完整的编译版本。

  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); 

结果如下:

 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 

编辑2016-05-24(两年后):

在同一台机器上使用实际的Java 8版本(U92)运行相同的testing:

 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 

似乎有顺序处理stream的速度加快,并行stream的开销更大。

当search“java 8 flatten”时,这是唯一提及的。 而且这也不是关于扁平化。 所以为了好,我只是把它留在这里

 .flatMap(Collection::stream) 

我也惊讶没有人给出了原来的问题是并发的java 8答案

 .collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll); 

如果您正在使用Eclipse集合 ,则可以使用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()是更一般的RichIterable.flatCollect()的特例 。

 MutableList<String> flattened = map.flatCollect(x -> x, Lists.mutable.empty()); 

注意:我是Eclipse集合的提交者。

由一位同事推荐:

 listOfLists.stream().flatMap(e -> e.stream()).collect(Lists.toList()) 

我喜欢它比forEach()更好。

不,没有更短的方法。 你必须使用一个循环。

2014年4月更新: Java 8终于出来了。 在新版本中,您可以使用Iterable.forEach方法遍历集合而不使用显式循环。

2017年11月更新:寻找现代解决scheme时偶然发现这个问题。 结束与reduce

 someMap.values().stream().reduce(new ArrayList(), (accum, list) -> { accum.addAll(list); return accum; }): 

这避免了取决于forEach(someList::addAll)的可变外部状态flatMap(List::stream)的开销。

如果你只是想遍历值,你可以避免所有这些addAll方法。

你所要做的就是编写一个封装你的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(); } } } 

地图贴图的一个很好的解决scheme是如果可能的话,将数据存储在番石榴的Table

https://github.com/google/guava/wiki/NewCollectionTypesExplained#table

因此,例如一个Map<String,Map<String,String>>被已经平坦化的Table<String,String,String>所取代。 实际上,文档说HashBasedTableTable的Hash实现,基本上由HashMap<R, HashMap<C, V>>