Ответ 1
Как насчет google guava?:
Maps.difference(map1,map2)
У меня есть две карты:
Map<String, Object> map1;
Map<String, Object> map2;
Мне нужно получить разницу между этими картами. Существует ли может быть apache utils, как получить эту разницу? На данный момент, кажется, нужно взять набор записей каждой карты и найти diff1 = set1 - set2 и diff2 = set2 - set1. После создания сводной карты = diff1 + diff2 Это выглядит очень неловко. Существует ли другой способ? Спасибо.
Как насчет google guava?:
Maps.difference(map1,map2)
Вот простой фрагмент, который вы можете использовать вместо массивной библиотеки 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;
}
Если я хорошо понял, вы пытаетесь вычислить симметричную разницу между двумя наборами записей карт.
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]
что неверно (и может быть описано как неудобно:))
В первом примере карта заполняется значениями int, которые автоматически в штучной упаковке соответствуют значениям Integer.
Класс Integer имеет собственную реализацию equals и hashCode.
Класс CustomInteger не реализует эти методы, поэтому наследует их от вездесущего Класс объекта.
API doc для метода removeAll из Установить интерфейс говорит следующее:
Удаляет из этого множества все его элементы, которые содержатся в указанной коллекции (дополнительная операция). Если указанная коллекция также является набором, эта операция эффективно модифицирует этот набор, так что его значение представляет собой асимметричную разность наборов двух наборов.
Чтобы определить, какие элементы содержатся в обеих коллекциях, метод removeAll использует метод equals для элемента коллекции.
И что метод catch: Integer equals возвращает true, если два числовых значения одинаковы, а метод equals объекта возвращает 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
Если он все еще немного нечеткий, я настоятельно рекомендую прочитать следующие уроки:
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
Существует MapDifference API Google Collections Library
, который предоставляет методы вроде:
boolean areEqual()
Возвращает true, если между двумя картами нет различий; то есть, если карты равны.
Map<K,MapDifference.ValueDifference<V>> entriesDiffering()
Возвращает немодифицируемую карту, описывающую ключи, которые отображаются на обеих картах, но с разными значениями.
Map<K,V> entriesInCommon()
Возвращает немодифицируемую карту, содержащую записи, которые отображаются на обеих картах; т.е. пересечение двух отображений.
Основываясь на примере Влада для работы с картами разных размеров
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;
}
Попробуйте использовать guava MapDifference.