說我有一個Map<? extends Object, List<String>>
平展收集
我可以得到地圖的價值很輕鬆了,和遍歷它來產生一個List<String>
。
for (List<String> list : someMap.values()) {
someList.addAll(list);
}
有沒有辦法將它壓扁一次?
List<String> someList = SomeMap.values().flatten();
說我有一個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);
如果我沒有錯,這實際上不建議 - https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html請參閱side-effect的部分。 >一般來說,行爲參數對流操作的副作用是不鼓勵的,因爲它們通常會導致無意中違反無狀態要求以及其他線程安全危害。因此,在這種情況下,最好使用'Collector.toList()' –
不,沒有更短的方法。你必須使用循環。
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)
開銷可變外部狀態。
如果你只是想通過值迭代,你能避免所有這些方法的addAll。
所有您需要做的是寫一個封裝你的地圖類,並實現了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();
}
}
}
如果您使用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集合的提交者。
使用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年5月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
看來有一個spee用於流的順序處理以及並行流的更大開銷。
當搜索「java 8 flatten」時,這是唯一提及的。而且它也不是關於扁平化。因此,對於偉大的好,我剛剛離開這裏
.flatMap(Collection::stream)
我也感到驚訝沒有人給Java併發8回答原來的問題是
.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
我相信'.collect(ArrayList :: new,ArrayList :: addAll,ArrayList :: addAll);'是正確的答案。 'flatMap()'在這種情況下沒有用。如果你需要在獲得一個流之前調用另一個參數方法(即調用'stream()'方法),'flatMap()'會很有用。然而,在這裏,我們已經有了一個對象,我們可以直接檢索一個流。 –
爲Map的子情況一個很好的解決方案的地圖是如果可能的話,將數據存儲在Guava的Table
中。
https://github.com/google/guava/wiki/NewCollectionTypesExplained#table
因此,例如,一個Map<String,Map<String,String>>
由Table<String,String,String>
這已經是flattend取代。事實上,文檔說HashBasedTable
,Table
的哈希實現,基本上是由一個HashMap<R, HashMap<C, V>>
一個同事建議的支持:
listOfLists.stream().flatMap(e -> e.stream()).collect(Lists.toList())
我喜歡它比的forEach更好的()。
您可以使用List :: stream的方法引用替換e - > e.stream()。應該快一點。 –
使用循環有什麼問題? –
@JoshM一無所有。但是,如果我可以使用內置的東西,我應該。我通常知道這些類型的問題的答案,但這次我不知道,所以我想我會問。 –