2017-07-18 94 views
1

我試圖地圖列表合併成一個單一的一個:合併地圖列表到一個單一的地圖

List<Map<String, List<Long>>> dataSet; 
Map<String, Set<Long>> uniqueSets = dataset.stream() 
    .flatMap(m -> m.entrySet().stream()) 
    .collect(Collectors.groupingBy(
     Map.Entry::getKey, 
     Collector.of(
      HashSet<Long>::new, 
      ..., 
      ... 
     ) 
    )); 

的想法是,uniqueSet應持有唯一的ID內的列表(多頭)每個集合(由String標識)。但我不確定...部件。

對於要求例子(JSON):

輸入:

[ 
    { 
     "Collection1": [1, 2, 3, 3], 
     "Collection2": [2, 3] 
    }, 
    { 
     "Collection1": [3, 4], 
     "Collection3": [1, 2] 
    } 
] 

輸出:

{ 
    "Collection1": [1, 2, 3, 4], 
    "Collection2": [2, 3], 
    "Collection3": [1, 2] 
} 
+1

如果這些應該是_unique,_你不想要一個集? –

+0

如果兩張地圖具有相同的關鍵字並具有不同的值,該怎麼辦?在兩次分開的運行中,您可能會得到兩個不同的結果(意思是說它不一致)。 – alfasin

+0

@alfasin我不確定你的意思。但是,如果兩個地圖具有相同的密鑰,則結果應該使用其列表的合併列表一次具有該密鑰。我的意思是這是整個想法。 – Mehran

回答

2

如果我明白你的問題,如果給這兩個地圖:

{Mike=[5, 6], Jack=[1, 2, 3]} 
{Fred=[7, 8], Jack=[4, 5]} 

您希望他們這樣的組合:

{Mike=[5, 6], Fred=[7, 8], Jack=[1, 2, 3, 4, 5]} 

在這裏你去:

Map<String, List<Long>> uniqueSets = dataset.stream() 
    .flatMap(m -> m.entrySet().stream()) 
    .collect(Collectors.groupingBy(
     Map.Entry::getKey, 
     Collector.of(
      ArrayList<Long>::new, 
      (list, item) -> list.addAll(item.getValue()), 
      (left, right) -> { left.addAll(right); return left; }) 
    )); 

即:

  • 你得到了供應商的權利:創建一個新的ArrayList累積值
  • 第二部分是累加器:需要一個容器(列表)和一個項目,並將項目添加到容器。請注意,這些項目是Map.Entry實例。
  • 最後一塊是一個組合,以兩個容器通過蓄能器填充合併成一個
+0

我不確定部分它需要的問題*唯一ID的列表*。如果第一張地圖上的「Jack」有'[1,2,3]',最後的'Jack'需要是'[1,2,3,4,5]還是'[1, 1,2,3,4,5]' –

+0

一個問題:不應該所有的部分都保持不變(不可變)嗎?我的意思是'list'和'left',它們在代碼中被修改。 – Mehran

+0

@Mehran只有在收集過程中創建的列表纔會發生變化,原始地圖內容將不變。累加器的目的是改變某些東西。至於組合器,[javadoc](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html#combiner--)明確指出可以摺疊一個一邊到另一邊。 – janos

1

您可以使用類似這樣

Map<String, List<Long>> uniqueSets = dataset.stream() 
      .flatMap(m -> m.entrySet().stream()) 
      .collect(Collectors.groupingBy(
        Map.Entry::getKey, 
        Collector.of(
          ArrayList<Long>::new, 
          (x, y) -> { x.addAll(y.getValue()); }, 
          (x, y) -> { 
           List<Long> r = new ArrayList<>(x); 
           r.addAll(y); 
           return r; 
          } 
        ) 
      )); 

編輯:

另一種方法是

Map<String, List<Long>> uniqueSets = dataset.stream() 
      .flatMap(m -> m.entrySet().stream()) 
      .flatMap(e -> e.getValue().stream().map(v -> new Pair(e.getKey(), v))) 
      .collect(Collectors.groupingBy(
        Pair::getKey, 
        Collectors.mapping(
          Pair::getValue, 
          Collectors.toList() 
        )) 
      ); 

但是,它需要對類(你可以找到許多圖書館同級)

class Pair { 
    final String key; 
    final Long value; 

    public Pair(String key, Long value) { 
     this.key = key; 
     this.value = value; 
    } 

    public String getKey() { 
     return key; 
    } 

    public Long getValue() { 
     return value; 
    } 
} 
0

怎麼是這樣的:

Map<String, Set<Long>> uniqueSets = new HashMap<>(); 
dataset.forEach(map -> map.forEach((string, list) -> { 
    if (uniqueSets.get(string) != null){ 
     list.forEach(id -> uniqueSets.get(string).add(id)); 
    }else{ 
     uniqueSets.put(string, new HashSet<>(list)); 
    } 
})); 
0

試試這個。

Map<String, List<Long>> r = 
    dataSet.stream() 
    .flatMap(e -> e.entrySet().stream()) 
    .flatMap(e -> e.getValue().stream() 
     .map(v -> new AbstractMap.SimpleEntry<String, Long>(e.getKey(), v))) 
    .distinct() 
    .collect(Collectors.groupingBy(
     Entry::getKey, 
     Collectors.mapping(Entry::getValue, Collectors.toList()))); 
System.out.println(dataSet); 
System.out.println(r); 

結果:

[{Collection1=[1, 2, 3, 3], Collection2=[2, 3]}, {Collection1=[3, 4], Collection3=[1, 2]}] 
{Collection1=[1, 2, 3, 4], Collection3=[1, 2], Collection2=[2, 3]} 
0

當你特別要求具有自定義收集一個基於流的解決方案,這裏有一個辦法:

List<Map<String, List<Long>>> dataSet; 
Map<String, Set<Long>> uniqueSets = dataset.stream() 
    .flatMap(m -> m.entrySet().stream()) 
    .collect(Collectors.groupingBy(
     Map.Entry::getKey, 
     Collector.of(
      HashSet<Long>::new, 
      (set, e) -> set.addAll(e.getValue()), 
      (left, right) -> { left.addAll(right); return left; }))); 

不過,我認爲有沒有流的更好的解決方案:

Map<String, Set<Long>> uniqueSets = new HashMap<>(); 

dataSet.forEach(map -> 
    map.forEach((k, v) -> uniqueSets.merge(
     k, 
     new HashSet<>(v), 
     (o, n) -> { o.addAll(n); return o; }))); 

這對地圖列表進行迭代,並且對於每個地圖,它遍歷其條目,以便對於每個地圖的每個鍵/值對,將該值轉換爲映射到關鍵字的HashSetuniqueSets地圖。這是通過Map.merge方法完成的,該方法在地圖中不存在的情況下創建條目,或者如果已經有該值的關鍵字,則將新值與舊值合併。爲了合併,我使用了Set.addAll