2016-03-24 14 views
7

Vorrei unire due Mappa con JAVA 8 Stream:Unisci Map <String, Lista <String> Java 8 Streaming

Map<String, List<String>> mapGlobal = new HashMap<String, List<String>>(); 
Map<String, List<String>> mapAdded = new HashMap<String, List<String>>(); 

Io cerco di usare questa implementazione:

mapGlobal = Stream.of(mapGlobal, mapAdded) 
       .flatMap(m -> m.entrySet().stream()) 
       .collect(Collectors.groupingBy(Map.Entry::getKey, 
         Collectors.mapping(Map.Entry::getValue,   
              Collectors.toList()) 
       )); 

Tuttavia, questa implementazione solo creare un risultato come:

Map<String, List<Object>>

Se una chiave non è contenuta nello mapGlobal, verrà aggiunta come una nuova chiave con l'elenco di stringhe corrispondente. Se la chiave è duplicata in mapGlobal e mapAdded, entrambi gli elenchi di valori saranno uniti come: A = {1, 3, 5, 7} e B = {1, 2, 4, 6} quindi A ∪ B = {1, 2, 3, 4, 5, 6, 7}.

risposta

7

È possibile eseguire questa operazione iterando su tutte le voci in mapAdded e unendole in mapGlobal.

Quanto segue scorre le voci di mapAdded chiamando forEach(action) dove l'azione consuma la chiave e il valore di ogni voce. Per ogni voce, chiamiamo merge(key, value, remappingFunction) su mapGlobal: questo creerà la voce sotto la chiave e il valore v se la chiave non esiste o invocherà la funzione di rimappatura data se già esisteva. Questa funzione prende le 2 liste di fondersi, che in questo caso, vengono prima aggiunto a un TreeSet per garantire sia unico e gli elementi allineati e riconvertito in un elenco:

mapAdded.forEach((k, v) -> mapGlobal.merge(k, v, (v1, v2) -> { 
    Set<String> set = new TreeSet<>(v1); 
    set.addAll(v2); 
    return new ArrayList<>(set); 
})); 

Se si desidera eseguire potenzialmente in parallelo , è possibile creare una pipeline Stream ottenendo il entrySet() e chiamando parallelStream() su di esso. Tuttavia, è necessario assicurarsi di utilizzare una mappa che supporti la concorrenza per mapGlobal, ad esempio ConcurrentHashMap.

ConcurrentMap<String, List<String>> mapGlobal = new ConcurrentHashMap<>(); 
// ... 
mapAdded.entrySet().parallelStream().forEach(e -> mapGlobal.merge(e.getKey(), e.getValue(), (v1, v2) -> { 
    Set<String> set = new TreeSet<>(v1); 
    set.addAll(v2); 
    return new ArrayList<>(set); 
})); 
+0

questa implementazione non utilizzerà i miglioramenti nelle raccolte di flussi. Entrambe le mappe possono essere enormi, quindi mi piacerebbe usare qualcosa come parallelStream(). È possibile. – ypriverol

+2

@ypriverol Sì, è possibile, ho modificato con quello. – Tunaki

+2

A meno che 'mapGlobal' sia un' ConcurrentMap', la sua mutazione all'interno di un flusso parallelo non è sicuro. – Misha

1

Uso di foreach over Mappa unire le mappe con il valore di ArrayList combinato nella mappa risultante.

public Map<String, ArrayList<String>> merge(Map<String, ArrayList<String>> map1, Map<String, ArrayList<String>> map2) { 
    Map<String, ArrayList<String>> map = new HashMap<>(); 
    map.putAll(map1); 

    map2.forEach((key , value) -> { 
     //Get the value for key in map. 
     ArrayList<String> list = map.get(key); 
     if (list == null) { 
      map.put(key,value); 
     } 
     else { 
      //Merge two list together 
      ArrayList<String> mergedValue = new ArrayList<>(value); 
      mergedValue.addAll(list); 
      map.put(key , mergedValue); 
     } 
    }); 
    return map; 
} 
1

L'implementazione originale non crea risultato simile Map<String, List<Object>>, ma Map<String, List<List<String>>>. È necessario disporre di ulteriore pipeline Stream per produrre Map<String, List<String>>.