web-dev-qa-db-ja.com

Javaでマップの違いを受け取る方法は?

私は2つの地図を持っています:

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

これらのマップの違いを受け取る必要があります。この違いを受け取る方法はApacheutilsに存在する可能性がありますか?今のところ、各マップのエントリセットを取得する必要があるようで、diff1 = set1-set2およびdiff2 = set2-set1が見つかりました。サマリーマップを作成した後= diff1 + diff2非常にぎこちなく見えます。別の方法がありますか?ありがとう。

18
user710818

google guava ?:

Maps.difference(map1,map2)
38
Koerr

大規模な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;
}

実際の例全体を確認してください

20
Vlad Holubiev

私がよく理解していれば、2つのマップエントリセット間の 対称差 2を計算しようとしています。

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

あなたが言及した厄介な振る舞いを考慮して、上記のコードの振る舞いを詳しく見てみましょう。たとえば、上記のリンクから数値例をとると、次のようになります。

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

図のように差を計算すると、次の出力が得られます。

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

与える:

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

これは正しい結果です。しかし、このようにすると:

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

同じアルゴリズムで次の出力が得られます。

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

これは正しくありません(そして厄介なものとして説明されるかもしれません:))

最初の例では、マップは整数値で埋められますが、整数値は自動的に ボックス化 になります。

クラスInteger には、 equals および hashCode メソッドの独自の実装があります。

クラスCustomIntegerはこれらのメソッドを実装していないため、 継承 遍在する オブジェクトクラス からメソッドを実装します。

Set interface からの removeAllメソッド のAPIドキュメントには次のように書かれています。

このセットから、指定されたコレクションに含まれているすべての要素を削除します(オプションの操作)。指定されたコレクションもセットである場合、この操作はこのセットを効果的に変更して、その値が2つのセットの非対称セット差になるようにします。

両方のコレクションに含まれる要素を判別するために、removeAllメソッドはコレクション要素のequalsメソッドを使用します。

そして、それがキャッチです。整数のequalsメソッドは、2つの数値が同じ場合にtrueを返しますが、オブジェクトのequalsメソッドは、sameの場合にのみtrueを返します。オブジェクト、例えば:

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

それでも少しあいまいな場合は、次のチュートリアルを読むことを強くお勧めします。

6
linski
    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
4
Amit Deshpande

構築 Vladの例 さまざまなサイズのマップを操作する

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;
    }
1
Baker

グアバの MapDifference を使用してみてください。

0
Emil