展平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>
所取代。 实际上,文档说HashBasedTable
, Table
的Hash实现,基本上由HashMap<R, HashMap<C, V>>