说我有Map<? extends Object, List<String>>
我可以很容易地获得地图的值,并迭代它以生成单个List<String>
。
for (List<String> list : someMap.values()) {
someList.addAll(list);
}
有没有办法一次性压扁它?
List<String> someList = SomeMap.values().flatten();
答案 0 :(得分:66)
使用Java 8,如果您不想自己实例化List
实例,就像在建议(和接受)的解决方案中那样
someMap.values().forEach(someList::addAll);
您可以通过使用此声明进行流式传输来完成所有操作:
List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());
顺便说一句,有趣的是,在Java 8上,接受的版本似乎确实是最快的。它的时间与
大致相同for (List<String> item : someMap.values()) ...
并且比纯流媒体解决方案更快。这是我的小测试代码。我明确地没有将其命名为基准,以避免由此引发的对基准缺陷的讨论。 ;)我做了两次测试,希望得到一个完整的编译版本。
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)运行相同的测试:
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
似乎对流的顺序处理有一个加速,并行流的开销更大。
编辑2018-10-18(两年后):
在同一台机器上使用现在的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
并行流的开销似乎更大。
答案 1 :(得分:54)
如果您使用的是Java 8,则可以执行以下操作:
someMap.values().forEach(someList::addAll);
答案 2 :(得分:34)
搜索&#34; java 8 flatten&#34;这是唯一提到的。而且它也不是关于压扁流。所以非常好,我就把它留在这里
.flatMap(Collection::stream)
我也很惊讶没有人给出原始问题的并发java 8答案
.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
答案 3 :(得分:7)
如果您使用的是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()
是一般情况RichIterable.flatCollect()的特例。
MutableList<String> flattened =
map.flatCollect(x -> x, Lists.mutable.empty());
注意:我是Eclipse Collections的提交者。
答案 4 :(得分:7)
同事建议:
>>> df.my_fruits[(df['fruit_a'] == 'vegetable') | (df['fruit_b'] == 'vegetable')] = 'not_fruit'
>>> df
fruit_a fruit_b my_fruits
0 apple vegetable not_fruit
1 banana apple fruit
2 vegetable vegeatble not_fruit
3 vegetable pineapple not_fruit
4 cherry pear fruit
我比forEach()更喜欢它。
答案 5 :(得分:5)
不,没有更短的方法。你必须使用循环。
2014年4月更新:Java 8终于问世了。在新版本中,您可以使用Iterable.forEach
方法遍历集合,而无需使用显式循环。
2017年11月更新:在寻找现代化解决方案时,偶然发现了这个问题。结束reduce
:
someMap.values().stream().reduce(new ArrayList(), (accum, list) -> {
accum.addAll(list);
return accum;
}):
这可以避免forEach(someList::addAll)
的开销取决于flatMap(List::stream)
的可变外部状态。
答案 6 :(得分:0)
如果您只想迭代值,可以避免使用所有这些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();
}
}
}
答案 7 :(得分:0)
地图地图的子案例的一个很好的解决方案是,如果可能的话,将数据存储在番石榴Table
中。
https://github.com/google/guava/wiki/NewCollectionTypesExplained#table
例如,Map<String,Map<String,String>>
被Table<String,String,String>
取代,而HashBasedTable
已经是平展的。事实上,文档说Table
,HashMap<R, HashMap<C, V>>
的哈希实现基本上由{{1}}支持
答案 8 :(得分:0)
展开功能:
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());
}