2016-04-25 6 views
4

name1/2合計値value1/2のキーでマップを作成する必要があります。Java 8ストリームはリストアイテムを縮小してマップに結合します

java 8ストリームを使用してこれを書き換える最もクリーンな方法は何でしょうか?

class Item { 

    private String name1; 
    private Integer value1; 
    private String name2; 
    private Integer value2; 

    public Item(final String name1, final Integer value1, final String name2, final Integer value2) { 
     this.name1 = name1; 
     this.value1 = value1; 
     this.name2 = name2; 
     this.value2 = value2; 
    } 
    //getters and setters 
} 
List<Item> list = Lists.newArrayList(
       new Item("aaa", 1, "bbb", 2), 
       new Item("bbb", 5, "ccc", 3), 
       new Item("aaa", 8, "bbb", 7), 
       new Item("bbb", 2, "aaa", 5)); 

Map<String, Integer> map = Maps.newHashMap(); 

for (Item item : list) { 
    map.merge(item.name1, item.value1, Integer::sum); 
    map.merge(item.name2, item.value2, Integer::sum); 
} 

System.out.println(map);//{aaa=14, ccc=3, bbb=16} 

答えて

7

可能な解決策は、2つのエントリによって行われたストリームに平坦マップに各アイテムである:各エントリには、名前および対応する値で構成されます。そして、これは同じキーを持つ値の値を合計することによってマップに集められます。両方の値を保持する組み込みのペアがないため、AbstractMap.SimpleEntryを使用できます。

Map<String, Integer> map = 
    list.stream() 
     .flatMap(i -> Stream.of(new AbstractMap.SimpleEntry<>(i.name1, i.value1), new AbstractMap.SimpleEntry<>(i.name2, i.value2))) 
     .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::sum)); 

また、カスタムcollectコールを使用する方が簡単かもしれません。アキュムレータは、forループ本体と同じように、各キーをマージします。トリッキーな部分はコンバイナです。この場合、2つのマップを結合し、2つ目のマップのエントリを繰り返して最初のマップにマージします。

Map<String, Integer> map = 
     list.stream() 
      .collect(
       HashMap::new, 
       (m, i) -> { 
        m.merge(i.name1, i.value1, Integer::sum); 
        m.merge(i.name2, i.value2, Integer::sum); 
       }, 
       (m1, m2) -> m2.forEach((k, v) -> m1.merge(k, v, Integer::sum)) 
      ); 
+1

私は好奇心を求めています。なぜ 'collect'の代わりに' reduce'を使用していますか?利点はありますか? – Flown

+1

@Flownいいえ、それは本当に同じです。 'flatMap'の呼び出しは、タプルホルダーを持つ必要があるため、簡単ではありません(理解しにくいかもしれません)。 'reduce'は単に' for'ループのように読む代替の解決策に過ぎません。リストが非常に大きく、確かではないにもかかわらず、パフォーマンスに違いがあるかもしれません。 – Tunaki

+3

質問は2番目のアプローチを対象としていました。私はOracleのチュートリアルで答えを見つけました。 'reduce'は不変で' collect'の変更可能な縮小で使われます。逐次実行においては、両方の減少が同じ挙動を示す。並列化された 'Stream'を使いたいのであれば' collect'を使うほうが良いでしょう:list.stream()。collect(HashMap :: new、(m、i) - > {m.merge(i.name1、i (m、m) - > m1.forEach((k、v) - > m2.merge) (k、v、Integer :: sum))) ' – Flown

関連する問題