Tengo dos mapas cuyas claves son String
sy cuyos valores son Set<MyObject>
. Dado dos Map
s, ¿cuál es la forma más fácil de combinarlos de manera que si dos claves son idénticas, el valor es una unión de los dos conjuntos. Puede suponer que los valores nunca son nulos y, si es útil, podemos hacer estos Map
s SortedMap
s.Fusionando dos mapas
Respuesta
Estamos hablando de HashMap
instancias. En ese caso, la búsqueda es O (1), por lo que puede tomar un mapa, iterar sobre las entradas de ese mapa, ver si el otro mapa contiene esa clave. Si no, solo agrega el conjunto. Si contiene la clave, tomar la unión de los dos conjuntos (por adding all elements de un conjunto a otro)
Para ilustrar con algo de código, en el que se utiliza un conjunto para tener la terminación automática en mi IDE
Map<String, Set<Double>> firstMap = new HashMap<String, Set<Double>>();
Map<String, Set<Double>> secondMap = new HashMap<String, Set<Double>>();
Set<Map.Entry<String, Set<Double>>> entries = firstMap.entrySet();
for (Map.Entry<String, Set<Double>> entry : entries) {
Set<Double> secondMapValue = secondMap.get(entry.getKey());
if (secondMapValue == null) {
secondMap.put(entry.getKey(), entry.getValue());
}
else {
secondMapValue.addAll(entry.getValue());
}
}
Esto omitirá las entradas que existen en secondMap pero no en firstMap – sam
sam - está editando el secondMap en su lugar, por lo que se cambiará secondMap. – JFK
puede usar - método addAll http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html Pero siempre hay este problema que - si sus dos mapas hash tienen cualquier tecla igual - luego anulará el valor de la clave del primer mapa hash con el valor de la clave del segundo mapa hash. Para estar en el lado seguro - cambie los valores clave - puede usar prefijo o sufijo en las teclas - (prefijo/sufijo diferente para el primer hash y diferentes prefijos/sufijos para el segundo hash) –
lo siguiente debe combinar una map1
en map2
(no probado):
for (Entry<String, Set<???>> entry : map1.entrySet())
{
Set<???> otherSet = map2.get(entry.getKey());
if (otherSet == null)
map2.put(entry.getKey(), entry.getValue ());
else
otherSet.addAll(entry.getValue());
}
no sé lo que ha parametrizado sus Set
s en, por lo tanto el <???>
: reemplazar según sea apropiado.
¿Qué tal esto (no probado):
Map<String,Set<Whatever>> m1 = // input map
Map<String,Set<Whatever>> m2 = // input map
Map<String,Set<Whatever>> ret = // new empty map
ret.putAll(m1);
for(String key : m2.keySet()) {
if(ret.containsKey(key)) {
ret.get(key).addAll(m2.get(key));
} else {
ret.put(key,m2.get(key));
}
}
Esta solución no modifica los mapas de entrada, y porque es corto y se basa en métodos de la API solamente, me parece muy legible.
Tenga en cuenta que putAll()
y addAll()
son métodos opcionales en Map
y Set
. En consecuencia (y para obtener la búsqueda O (1)), recomendaría usar HashMap
y HashSet
.
Tenga en cuenta que, como ni HashSet
ni HashMap
están sincronizados, tendrá que buscar alguna otra solución si desea un código de seguridad de subprocesos.
Algo como esto (no probado):
// Assume all maps are of the same generic type.
public static Map<String, Set<MyObject>> mergeAll(Map m1, Map m2) {
Map<String, Set<MyObject>> merged = new HashMap();
// Merge commom entries into the new map.
for (Map.Entry<String, Set<MyObject>> entry : m1.entrySet()) {
String key = entry.getKey();
Set<MyObject> s1 = new HashSet(entry.getValue());
Set<MyObject> s2 = m2.get(key);
if (s2 != null) s1.addAll(s2);
merged.put(key, s1);
}
// Add entries unique to m2 to the new map.
for (String key : m2.keys()) {
if (!s1.containsKey(key)) merged.put(key, new HashSet(m2.get(key)));
}
return merged;
}
Tenga en cuenta que esta solución no muta cualquiera de sus argumentos.
¿Qué pasa con las claves que están en ' m2' pero no en 'm1'? –
Llama a 'm2.getValue()', pero 'm2' es un' Map' y por lo tanto no tiene el método 'getValue()'. –
@MichaelMcGowan: Ah, claro, que fija también (! Vaya, ver lo que sucede cuando intento de codificar la parte superior de la cabeza) – maerics
Map<Integer,String> m1=new HashMap<Integer,String>();
Map<Integer,String> m2=new HashMap<Integer,String>();
m1.put(1,"one");
m1.put(2,"two");
m2.put(3,"three");
m2.put(2,"two");
Set<Integer> s=m2.keySet();
for(int i:s){
if(m1.get(i)==null){
m1.put(i,m2.get(i));
}
}
System.out.println(m1);
Ésta es sencillo programa que explica cómo combinar dos mapas – user3301756
Tenga en cuenta que todas las demás respuestas con el tiempo aumentar los conjuntos originales que puede que no desee para todos los casos de uso, si no quiere que sólo tiene que utilizar un tercer mapa como salida y crear un nuevo conjunto para cada tecla
public static void merge2Maps(Map<String, Set<Double>> a, Map<String, Set<Double>> b, Map<String, Set<Double>> c){
for (Map.Entry<String, Set<Double>> entry : a.entrySet()) {
Set<Double> set = new HashSet<Double>();
c.put(entry.getKey(), set);
set.addAll(entry.getValue());
}
for (Map.Entry<String, Set<Double>> entry : b.entrySet()) {
String key = entry.getKey();
Set<Double> set = c.get(key);
if (set == null) {
set = new HashSet<Double>();
c.put(entry.getKey(), set);
}
set.addAll(entry.getValue());
}
}
puede hacer esto con un stream con bastante facilidad:
Map<T, Set<U>> merged = Stream.of(first, second)
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> {
HashSet<U> both = new HashSet<>(a);
both.addAll(b);
return both;
}));
esto divide los mapas en sus Entry
s y luego se une a ellos con un Collector
que resolves duplicates agregando ambos valores a un nuevo HashSet
.
Esto también funciona para cualquier cantidad de mapas.
Algunas variaciones que producen el mismo resultado:
Stream.of(first, second).flatMap(m -> m.entrySet().stream())
.collect(...);
Stream.concat(first.entrySet().stream(), second.entrySet().stream())
.collect(...); //from comment by Aleksandr Dubinsky
El tercer parámetro para Collectors.toMap
no es necesario si no hay llaves duplicadas.
Hay otro Collectors.toMap
con un cuarto parámetro que le permite decidir el tipo de Map
recopilado.
Un poco más concisa es usar ' Stream.concat (first.entrySet(). Stream(), second.entrySet(). Stream()) 'y evite' map' y 'flatMap'. –
Si desea terminar con las estructuras de datos inmutables para evitar la manipulación de su mapa fusionada y casos Set del mapa, entonces puede tomar este enfoque. Esta solución usa la biblioteca de Guava de Google.
public <K,T> Map<K, Set<T>> mergeToImmutable (
final Map<K, Set<T>> left,
final Map<K, Set<T>> right)
{
return Maps.toMap(
Sets.union(
checkNotNull(left).keySet(),
checkNotNull(right).keySet()
),
new Function<K, Set<T>>() {
@Override
public Set<T> apply (K input) {
return ImmutableSet.<T>builder()
.addAll(MoreObjects.firstNonNull(left.get(input), Collections.<T>emptySet()))
.addAll(MoreObjects.firstNonNull(right.get(input), Collections.<T>emptySet()))
.build();
}
}
);
}
Si se define un método para unir no nulos Set
s como:
static <T> Set<T> union(Set<T>... sets) {
return Stream.of(sets)
.filter(s -> s != null)
.flatMap(Set::stream)
.collect(Collectors.toSet());
}
entonces la fusión de dos mapas m1
y m2
tienen Set<V>
valores se puede realizar de la siguiente manera:
Map<String, V> merged
= union(m1.keySet(), m2.keySet())
.stream()
.collect(Collectors.toMap(k -> k, k -> union(m1.get(k), m2.get(k))));
O aún más simple:
Map<String, V> merged = new HashMap<>();
for (String k : union(m1.keySet(), m2.keySet())
merged.put(k, union(m1.get(k), m2.get(k)));
<K, V> Map<K, List<V>> mergeMapOfLists(Stream<Map<K, List<V>>> stream) {
return stream
.map(Map::entrySet) // convert each map to set of map's entries
.flatMap(Collection::stream) // convert each map entry to stream and flat them to one stream
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue,
(list1, list2) -> {
list1.addAll(list2);
return list1;
})); // convert stream to map; if key is duplicated execute merge fuction (append exisitng list with elements from new list)
}
static void mergeSet(Map<String, Set<String>> map1, Map<String, Set<String>> map2) {
map1.forEach((key1, value1) -> {
map2.merge(key1, value1, (key2, value2) -> key2).addAll(value1);
});
}
- 1. Mapas: fusionando múltiples maquiales de diferentes tamaños
- 2. fusionando dos consultas SELECT
- 3. Fusionando dos imágenes
- 4. fusionando dos archivos
- 5. Fusionando dos documentos JSON con Jackson
- 6. Fusionando dos selecciones de jQuery
- 7. fusionando dos objetos en C#
- 8. Fusionando dos aplicaciones de rieles
- 9. Fusionando dos matrices por índice
- 10. fusionando dos dataframes en R
- 11. Fusionando dos arrays en .NET
- 12. Fusionando dos matrices en Bash
- 13. Combinar dos mapas STL
- 14. Diferencia entre dos mapas
- 15. intersección de dos mapas STL
- 16. Fusionando dos UIImages más rápido que CGContextDrawImage
- 17. Ruby fusionando dos matrices en una
- 18. std :: merge fusionando dos std :: vector coredump
- 19. Fusionando dos objetos complejos en PHP
- 20. Fusionando dos IEnumerable <T> s
- 21. Fusionando dos Colección <T>
- 22. Fusionando dos imágenes en C# /. NET
- 23. Combinar dos mapas en un MultiMap
- 24. Mezclar píxeles de Dos mapas de bits
- 25. Combinar dos mapas de bits en Android
- 26. Fusionando dos archivos por una sola columna en Unix
- 27. fusionando el contenido de dos tablas sin duplicar el contenido
- 28. Fusionando dos archivos de base de datos SQLite (C# .NET)
- 29. Fusionando dos o más archivos .wav en android
- 30. Creando un System.Configuration.Configuration fusionando dos archivos de configuración?
Si usted tiene la posibilidad de utilizar guayabas [Multimapa] (https://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap), sólo tiene que evitar el problema, y la fusión es tan simple como putAll (Multimap otro). – Dag
similares, posiblemente duplicados: http://stackoverflow.com/questions/4299728/how-can-i-combine-two-hashmap-objects-containing-the-same-types –
debe ser fácil de hacer con el [Mapa de combinación ] (https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#merge-KV-java.util.function.BiFunction-) método. – Roland