2012-10-04 18 views
15

Mam dwie mapy:Jak otrzymać różnicę map w Java?

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

muszę otrzymać różnicę między tymi mapami. Czy może istnieć apache utils, jak otrzymać tę różnicę? Na razie wydaje się, że trzeba pobrać zestaw wpisów dla każdej mapy i znaleźć diff1 = set1 - set2 i diff2 = set2- set1. Po utworzeniu mapy podsumowania = diff1 + diff2 Wygląda bardzo niezręcznie. Czy istnieje inny sposób? Dzięki.

Odpowiedz

33

Jak o google guava:

Maps.difference(map1,map2) 
+2

Dzięki. Myślałem o guava, ale do tego potrzebna jest nowa biblioteka w projekcie, nie rób tego. – user710818

+6

@ user710818 Nie żałowałbyś tego - jest to świetna biblioteka – vitaly

+3

@ user710818 Powinieneś użyć tego w swoim projekcie – 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

odpowiedzi nie wygląda poprawne. Mapa1 może zawierać mapę2 lub mapę2 może zawierać mapę1 lub być równe lub różnice w dowolnym kierunku mogą istnieć. – user710818

+0

@ user710818 Sprawdź zaktualizowaną odpowiedź. –

2

Jest MapDifference API przez Google Collections Library co naraża metod takich jak:

boolean areEqual() 

Zwraca true, jeśli nie ma różnic między tymi dwoma mapami; to znaczy, jeśli mapy są równe.

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

Powoduje odtworzenie niemodyfikowalnej mapy opisującej klucze pojawiające się na obu mapach, ale z różnymi wartościami.

Map<K,V> entriesInCommon() 

Zwraca mapę niemodyfikowalną zawierającą wpisy pojawiające się na obu mapach; to znaczy przecięcie dwóch map.

5

Jeśli dobrze rozumiem, próbujesz obliczyć symmetric difference pomiędzy zestawami wejściowymi dwóch map.

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); 

Biorąc pod uwagę dziwne zachowanie, o którym wspomniałeś, przyjrzyjmy się powyższemu zachowaniu kodu. Na przykład, jeśli weźmiemy przykład liczbowy z wyżej podanym linku:

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); 

Po obliczyć różnicę, jak pokazano, wyjście:

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

daje:

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

który jest poprawny wynik. Ale, jeśli robimy to tak:

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)); 

ten sam algorytm daje następujący wynik:

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

co nie jest poprawne (i może być opisana jako niewygodne :))

W pierwszy przykład mapa jest wypełniona wartościami int, które automatycznie są boxed na wartości całkowite.

ma własną implementację metod equals i hashCode.

Klasa CustomInteger nie implementuje tych metod, dzięki czemu są one inherits z wszechobecnego .

Doc API dla removeAll method z Set interface mówi co następuje:

Usuwa z tego zbioru wszystkich jego elementów, które są zawarte w określonej kolekcji (opcjonalnie operacja). Jeśli określona kolekcja jest również zbiorem, ta operacja efektywnie modyfikuje ten zbiór, tak aby jego wartość stanowiła asymetryczną różnicę zestawu dwóch zestawów.

Aby określić, które elementy znajdują się w obu kolekcjach, metoda removeAll używa metody równości elementu kolekcji.

I haczyk: całkowita na równe sposób powraca do sytuacji, gdy obie wartości numeryczne są takie same, podczas gdy obiektu jest równy sposób powraca prawdą tylko wtedy, gdy obiekt sam np :

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 

Jeśli to wciąż nieco rozmyty Sugeruję czytania następujące tutoriale:

+0

powyższa odpowiedź bardziej elegancko rozwiązała obliczenia różnicowe! – linski

16

Oto prosty fragment można użyć zamiast ogromna biblioteka Guawy:

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

Opierając się na Vlad's example do pracy z mapami o różnych rozmiarach

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; 
    } 
Powiązane problemy