2012-10-04 83 views
15

Tengo dos mapas:¿Cómo recibir la diferencia de mapas en java?

Map<String, Object> map1; 
Map<String, Object> map2; 

necesito para recibir diferencia entre estos mapas. ¿Existiría apache utiliza cómo recibir esta diferencia? Por ahora parece necesario tomar el conjunto de entrada de cada mapa y se encuentra diff1 = set1 - set2 y diff2 = set2 - set1. Después de crear el mapa resumen = diff1 + diff2 Parece muy torpe. ¿Existe otra forma? Gracias.

Respuesta

33

¿Qué tal google guava:?

Maps.difference(map1,map2) 
+2

Gracias. Pensé en la guayaba, pero para esto necesito introducir una nueva biblioteca en el proyecto, pero no hagas esto. – user710818

+6

@ user710818 No se arrepentirá, es una gran biblioteca – vitaly

+3

@ user710818 Debe usarla en su proyecto – Koerr

4
Set<Entry<String, Object>> diff = new HashSet<Entry<String, Object>>((map1.entrySet())); 
    diff.addAll(map2.entrySet());//Union 
    Set<Entry<String, Object>> tmp = new HashSet<Entry<String, Object>>((map1.entrySet())); 
    tmp.retainAll(map2.entrySet());//Intersection 
    diff.removeAll(tmp);//Diff 
+1

respuesta no parece correcta. Map1 podría contener map2 o map2 puede contener map1, o ser igual o puede existir una diferencia en cualquier dirección. – user710818

+0

@ user710818 Compruebe la respuesta actualizada. –

2

Hay una API MapDifference por Google Collections Library que expone métodos como:

boolean areEqual() 

devuelve verdadero si no hay diferencias entre los dos mapas; es decir, si los mapas son iguales.

Map<K,MapDifference.ValueDifference<V>> entriesDiffering() 

Devuelve un mapa inmodificable que describe las teclas que aparecen en ambos mapas, pero con diferentes valores.

Map<K,V> entriesInCommon() 

Devuelve un mapa no modificable que contiene las entradas que aparecen en ambos mapas; es decir, la intersección de los dos mapas.

5

Si lo entendí bien, está tratando de calcular symmetric difference entre los dos juegos de entradas de mapas.

Map<String, Object> map1; 
Map<String, Object> map2; 

Set<Entry<String, Object>> diff12 = new HashSet<Entry<String, Object>>(map1.entrySet()); 
Set<Entry<String, Object>> diff21 = new HashSet<Entry<String, Object>>(map2.entrySet()); 
Set<Entry<String, Object>> result; 

diff12.removeAll(map2.entrySet()); 
diff21.removeAll(map1.entrySet()); 
diff12.addAll(diff21); 

Teniendo en cuenta el comportamiento extraño que usted ha mencionado, vamos a echar un vistazo más de cerca el comportamiento de código de seguridad. Por ejemplo, si tomamos el ejemplo numérico desde el vínculo anterior:

Map<String, Object> map1 = new HashMap<String, Object>(); 
map1.put("a", 1); 
map1.put("b", 2); 
map1.put("c", 3); 
map1.put("d", 4); 

Map<String, Object> map2 = new HashMap<String, Object>(); 
map2.put("a", 1);  
map2.put("d", 4); 
map2.put("e", 5); 

Después de calcular la diferencia como se muestra, la salida:

System.out.println(Arrays.deepToString(diff12.toArray())); 

da:

[e=5, c=3, b=2] 

cual es el resultado correcto Pero, si lo hacemos así:

public class CustomInteger { 
    public int val; 

    public CustomInteger(int val) { 
     this.val = val; 
    } 

    @Override 
    public String toString() { 
     return String.valueOf(val); 
    }   
} 

map1.put("a", new CustomInteger(1)); 
map1.put("b", new CustomInteger(2)); 
map1.put("c", new CustomInteger(3)); 
map1.put("d", new CustomInteger(4)); 

map2.put("a", new CustomInteger(1));  
map2.put("d", new CustomInteger(4)); 
map2.put("e", new CustomInteger(5)); 

el mismo algoritmo da el siguiente resultado:

[e=5, a=1, d=4, d=4, b=2, a=1, c=3] 

que no es correcta (y que podría describirse como incómoda :))

En el primer ejemplo que el mapa se llena con valores int que son automáticamente boxed a valores enteros.

The class Integer tiene su propia implementación de los métodos equals y hashCode.

La clase CustomInteger no implementa estos métodos por lo que inherits de la omnipresencia Object class.

El doc API para el removeAll method de la Set interface dice lo siguiente:

elimina de este conjunto de todos los elementos que están contenidos en la colección especificada (operación opcional). Si la colección especificada también es un conjunto, esta operación modifica efectivamente este conjunto de modo que su valor sea la diferencia del conjunto asimétrico de los dos conjuntos.

Con el fin de determinar qué elementos están contenidos en ambas colecciones, el método removeAll utiliza el método equals del elemento de colección.

Y ese es el problema: Entero de igual método devuelve true si los dos valores numéricos son los mismos, mientras que el Objeto de igual método devolverá verdadero sólo si es el objeto misma, por ejemplo, :

Integer a = 1; //autoboxing 
Integer b = new Integer(1); 
Integer c = 2; 

a.equals(b); // true 
a.equals(c); // false 

CustomInteger d = new CustomInteger(1); 
CustomInteger e = new CustomInteger(1); 
CustomInteger f = new CustomInteger(2); 

d.equals(e); //false 
d.equals(f) // false 

d.val == e.val //true 
d.val == f.val //false 

Si aún así es un poco difusa que sugieren fuertemente la lectura de los siguientes tutoriales:

+0

¡la respuesta anterior ha resuelto de forma más elegante el cálculo de la diferencia! – linski

16

Aquí hay un fragmento simple que puede utilizar en lugar de biblioteca masiva de Guava:

public static <K, V> Map<K, V> mapDifference(Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right) { 
    Map<K, V> difference = new HashMap<>(); 
    difference.putAll(left); 
    difference.putAll(right); 
    difference.entrySet().removeAll(right.entrySet()); 
    return difference; 
} 

Check out the whole working example

1

Basándose en Vlad's example para trabajar con mapas de diferentes tamaños

public static <K, V> Map<K, V> mapDiff(Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right) { 
     Map<K, V> difference = new HashMap<>(); 
     difference.putAll(left); 
     difference.putAll(right); 

     difference.entrySet().removeAll(left.size() <= right.size() ? left.entrySet() : right.entrySet()); 

     return difference; 
    } 
Cuestiones relacionadas