67

私が持っていると言うMap<? extends Object, List<String>>

マップの値を十分に簡単に取得し、それを反復処理して単一の を生成できList<String>ます。

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

一発で平らにする方法はありますか?

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

9 に答える 9

79

ListJava 8 を使用し、提案された (そして受け入れられた) ソリューションのように、自分でインスタンスをインスタンス化したくない場合

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()) ...

純粋なストリーミング ソリューションよりもはるかに高速です。これが私の小さなテストコードです。ベンチマークの欠陥に関する議論を避けるため、ベンチマークとは明示的に名前を付けません。;) できれば完全にコンパイルされたバージョンを取得するために、すべてのテストを 2 回行います。

    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 を編集 (2 年後):

同じマシンで実際の 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 を編集 (4 年後):

同じマシンで 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

並列ストリーミングのオーバーヘッドの方が大きいようです。

2020-05-22 を編集 (6 年後):

別のマシンで 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

これは別のマシンで行われたことに注意してください(ただし、同等だと思います)。並列ストリーミングのオーバーヘッドは、以前よりかなり小さくなっているようです。

于 2014-04-30T13:52:14.407 に答える
38

「java 8 flatten」を検索すると、これが唯一の言及です。また、ストリームを平坦化することでもありません。とても良いので、ここに置いておきます

.flatMap(Collection::stream)

また、元の質問に対するJava 8の同時回答を誰も与えていないことにも驚いています。

.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
于 2015-05-04T22:58:22.113 に答える
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 コレクションのコミッターです。

于 2013-08-20T02:42:06.520 に答える
0

Map of Maps のサブケースの適切な解決策は、可能であれば、データを Guava の に保存することTableです。

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

したがって、たとえば aは、すでに flattend されているものMap<String,Map<String,String>>に置き換えられます。Table<String,String,String>実際、ドキュメントによるとHashBasedTableTableの Hash 実装は、本質的にはHashMap<R, HashMap<C, V>>

于 2016-03-02T08:02:31.383 に答える