Java Различия между картами, включая значения

user7294900 спросил: 28 апреля 2018 в 09:23 в: java

Мне нужно найти только различия между двумя картами, в то время как разные могут быть по отсутствующему ключу или по разному значению для ключа.

Я нахожу общий ответ Различия между картами

sources.removeAll(targets) ... leaves only entries in sources that are only in sources, not in target

, тогда как

sources.retainAll(targets) ... leaves only entries that are in both sets

Но я не уверен, что это лучше чем следующий код, потому что мне нужно проверить отдельно от существования ключа, что разные значения

    Map<K, V> updatedMap = new EnumMap<>(K.class);
    for (Map.Entry<K, V> finalSet : secondMap.entrySet()) {
        K currentKey = finalSet.getKey();
        if (!firstMap.containsKey(currentKey) || firstMap.get(currentKey) != finalSet.getValue()) {
            updatedMap.put(currentKey, finalSet.getValue());
            firstMap.remove(currentKey);
        }
    }
    for (Map.Entry<K, V> currentSet : firstMap.entrySet()) {
        K currentKey = currentSet.getKey();
        if (!secondMap.containsKey(currentKey)) {
            updatedMap.put(currentKey, currentSet.getValue());
        } else if (secondMap.get(currentKey) != currentSet.getValue()) {
            updatedMap.put(currentKey, secondMap.get(currentKey));
        }
    }

Является ли их лучшим способом найти различия между картами, включая значения?


2 ответа

Есть решение
Eugene ответил: 28 апреля 2018 в 10:14

Ну, вы можете сравнить Entry s с Map, потому что этот класс переопределяет equals/hashCode так, как вы хотите. Не совсем понятно, какие записи вы хотите сохранить, те из левая карта или правая карта или любая из них.

Например это может быть сделано с помощью:

Map<Integer, String> allDifs = 
             Sets.symmetricDifference(left.entrySet(), right.entrySet())
                 .stream()
                 .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

С другой стороны, если вы просто хотите сохранить записи из второго (справа) Map:

 Map<Integer, String> result = 
             Sets.difference(right.entrySet(), left.entrySet())
                 .stream()
                 .collect(Collectors.toMap(Entry::getKey, Entry::getValue));    System.out.println(result); 

Очевидно, вам нужно guava и java-8 для этого ...

EDIT

то, что вы на самом деле хотите, не достижимо с помощью Collectors.toMap, но вы можете сделать это с помощью

    Map<Integer, String> result = new HashMap<>();
    Sets.symmetricDifference(right.entrySet(), left.entrySet())
            .stream()
            .forEachOrdered(x -> {
                String previousValue = result.putIfAbsent(x.getKey(), x.getValue());
                if (previousValue != null) {
                    result.replace(x.getKey(), right.get(x.getKey()));
                }
            });
user7294900 ответил: 28 апреля 2018 в 09:41
У меня есть гуава в зависимости от Джерси, поэтому я проверю это
user7294900 ответил: 28 апреля 2018 в 09:53
symmetricDifference - это почти то, что мне нужно, но я получил тестирование java.lang.IllegalStateException: Duplicate key NA
Eugene ответил: 28 апреля 2018 в 09:55
@ user7294900, что, вероятно, происходит потому, что у вас есть два Key как NA, поэтому Collectors.toMap не удается ... в этом случае - что вы хотите делать, когда дублируете ключи присутствуют? это то, о чем вам нужно подумать. например, вы могли бы получить результат как Map<String, List<String>>, чтобы сохранить повторяющиеся значения ключа в List или всегда сохранять first (или last ) один ... так какой из них?
user7294900 ответил: 28 апреля 2018 в 09:56
Второе значение карты должно преодолевать / отменять
Eugene ответил: 28 апреля 2018 в 10:14
@ user7294900 см. Редактировать
Olivier Grégoire ответил: 28 апреля 2018 в 10:59

Их вики объясняют, как это работает, но вы можете найти ниже решение своей проблемы.

Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Map<String, Integer> right = ImmutableMap.of("b", 2, "c", 4, "d", 5);
MapDifference<String, Integer> diff = Maps.difference(left, right);Map<String, Integer> output = new HashMap<>();
output.putAll(diff.entriesOnlyOnLeft());
output.putAll(diff.entriesOnlyOnRight());
for (Map.Entry<String,MapDifference.ValueDifference<Integer>> e: diff.entriesDiffering().entrySet()) {
  // Java 10 and later : for (var e: diff.entriesDiffering().entrySet())
  output.put(e.getKey(), e.getValue().rightValue());
}
System.out.println(output); // {a=1, c=4, d=5}
user7294900 ответил: 28 апреля 2018 в 10:30
Я хочу, чтобы карта имела только различия, где вторая - первая карта, в вашем примере с (a, 1, c, 4, d, 5)
Olivier Grégoire ответил: 28 апреля 2018 в 10:34
Если у вас есть две карты, left и right, вы знаете, какие элементы только из left вы хотите, а не хотите, и то же самое для right. Все, что вам нужно сделать, это взять entriesOnlyOnLeft(). Я уточню свой ответ с вашими точными требованиями.
user7294900 ответил: 28 апреля 2018 в 10:55
Я использую java 8 (не 9), и ваш код не компилируется: нет .entries() и .getRight()
Olivier Grégoire ответил: 28 апреля 2018 в 11:02
@ user7294900 Исправлено. Я сделал слишком много ярлыков и не тестировал. Текущий код полностью протестирован.
user7294900 ответил: 28 апреля 2018 в 11:03
Спасибо, это работает, но я думаю, что я выберу symmetricDifference, который сохранит больше строк кода