Как сравнить две карты по их значениям

Как сравнить две карты по их ценности? У меня есть две карты, содержащие равные значения, и я хочу сравнить их по их значениям. Вот пример:

    Map a = new HashMap();
    a.put("foo", "bar"+"bar");
    a.put("zoo", "bar"+"bar");

    Map b = new HashMap();
    b.put(new String("foo"), "bar"+"bar");
    b.put(new String("zoo"), "bar"+"bar");

    System.out.println("equals: " + a.equals(b));            // obviously false

    .... what to call to obtain a true?

[[ EDIT: кто-то, пожалуйста, отредактируйте и исправьте этот вопрос, чтобы он означал то, что он на самом деле должен означать. Код выше печатает "true", а не"false". ]]

очевидно, что реализовать сравнение не сложно, достаточно сравнить все ключи и связанные с ними значения. Я не верю, что я первый, кто это сделает, поэтому уже должна быть библиотека функций либо на java, либо в одной из Джакарты.Коммонс библиотеки.

спасибо

12 ответов


ваши попытки построить различные строки с помощью конкатенации потерпят неудачу, поскольку она выполняется во время компиляции. Обе эти карты имеют одну пару; каждая пара будет иметь" foo "и" barbar " в качестве ключа/значения, используя одну и ту же строковую ссылку.

предполагая, что вы действительно хотите сравнить наборы значений без какой-либо ссылки на ключи, это просто случай:

Set<String> values1 = new HashSet<>(map1.values());
Set<String> values2 = new HashSet<>(map2.values());
boolean equal = values1.equals(values2);

Это возможно сравнение map1.values() С map2.values() будет работать - но также возможно, что порядок, в котором они возвращаются, будет использоваться в сравнении равенства, чего вы не хотите.

обратите внимание, что использование набора имеет свои проблемы-потому что приведенный выше код будет считать отображение {"a":"0", "b":"0"} и {"c":"0"} равным... в конце концов, наборы значений равны.

Если вы можете предоставить более строгое определение того, что вы хотите, будет легче убедиться, что мы дадим вам правильный ответ.


правильный способ сравнить карты для равенства значений:

  1. проверить, что карты имеют одинаковый размер(!)
  2. набор ключи С одной карты
  3. для каждого ключа из того набора, который вы получили, проверьте, что значение, полученное из каждой карты для этого ключа, одинаково (если ключ отсутствует на одной карте, это полный сбой равенства)

другими словами (минус обработка ошибок):

boolean equalMaps(Map<K,V>m1, Map<K,V>m2) {
   if (m1.size() != m2.size())
      return false;
   for (K key: m1.keySet())
      if (!m1.get(key).equals(m2.get(key)))
         return false;
   return true;
}

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

  • сделать их Collection<V> values() вид
  • обернуть в List<V>
  • Collections.sort списки
  • Проверьте, являются ли два списка equals

что-то вроде этого работает (хотя его границы типа могут быть улучшены):

static <V extends Comparable<V>>
boolean valuesEquals(Map<?,V> map1, Map<?,V> map2) {
    List<V> values1 = new ArrayList<V>(map1.values());
    List<V> values2 = new ArrayList<V>(map2.values());
    Collections.sort(values1);
    Collections.sort(values2);
    return values1.equals(values2);
}

теста:

Map<String, String> map1 = new HashMap<String,String>();
map1.put("A", "B");
map1.put("C", "D");

Map<String, String> map2 = new HashMap<String,String>();
map2.put("A", "D");
map2.put("C", "B");

System.out.println(valuesEquals(map1, map2)); // prints "true"

это O(N log N) из-за Collections.sort.

Читайте также:


чтобы проверить, если ключи равны легче, потому что они Set<K>:

map1.keySet().equals(map2.keySet())

Читайте также:


все они возвращают равные. Они на самом деле не делают сравнение, которое полезно для сортировки. Это будет вести себя больше как компаратор:

private static final Comparator stringFallbackComparator = new Comparator() {
    public int compare(Object o1, Object o2) {
        if (!(o1 instanceof Comparable))
            o1 = o1.toString();
        if (!(o2 instanceof Comparable))
            o2 = o2.toString();
        return ((Comparable)o1).compareTo(o2);
    }
};

public int compare(Map m1, Map m2) {
    TreeSet s1 = new TreeSet(stringFallbackComparator); s1.addAll(m1.keySet());
    TreeSet s2 = new TreeSet(stringFallbackComparator); s2.addAll(m2.keySet());
    Iterator i1 = s1.iterator();
    Iterator i2 = s2.iterator();
    int i;
    while (i1.hasNext() && i2.hasNext())
    {
        Object k1 = i1.next();
        Object k2 = i2.next();
        if (0!=(i=stringFallbackComparator.compare(k1, k2)))
            return i;
        if (0!=(i=stringFallbackComparator.compare(m1.get(k1), m2.get(k2))))
            return i;
    }
    if (i1.hasNext())
        return 1;
    if (i2.hasNext())
        return -1;
    return 0;
}

этот вопрос старый, но все еще актуален.

Если вы хотите сравнить две карты по их значениям, соответствующим их ключам, вы можете сделать следующее:

public static <K, V> boolean mapEquals(Map<K, V> leftMap, Map<K, V> rightMap) {
    if (leftMap == rightMap) return true;
    if (leftMap == null || rightMap == null || leftMap.size() != rightMap.size()) return false;
    for (K key : leftMap.keySet()) {
        V value1 = leftMap.get(key);
        V value2 = rightMap.get(key);
        if (value1 == null && value2 == null)
            continue;
        else if (value1 == null || value2 == null)
            return false;
        if (!value1.equals(value2))
            return false;
    }
    return true;
}

Так как вы спросили о готовых Api ... Коммонс хорошо Апача. коллекция библиотека имеет CollectionUtils класс, который предоставляет простые в использовании методы для обработки/проверки коллекции, такие как пересечение, разница и объединение.


Я не думаю, что есть инструмент" apache-common-like " для сравнения карт, поскольку равенство 2 карт очень неоднозначно и зависит от потребностей разработчика и реализации Карты...

для примера, если вы сравниваете два хэш-карты в java: - Вы можете просто сравнить ключ / значения одинаковы - Вы также можете сравнить, если ключи заказаны таким же образом - Вы также можете сравнить если оставшаяся емкость одинакова ... Вы можете сравнить много вещи!

что такой инструмент будет делать при сравнении 2 различных реализаций карты, таких, что: - Одна карта позволяет нулевые ключи - Другое исключение выполнения броска на map2.get (null)

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


Если вы предполагаете, что могут быть повторяющиеся значения, единственный способ сделать это-поместить значения в списки, отсортировать их и сравнить списки, а именно:

List<String> values1 = new ArrayList<String>(map1.values());
List<String> values2 = new ArrayList<String>(map2.values());
Collections.sort(values1);
Collections.sort(values2);
boolean mapsHaveEqualValues = values1.equals(values2);

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


результат equals в вашем примере явно ложен, потому что вы сравниваете карту a с некоторыми значениями в ней с пустой картой b (возможно, ошибка копирования и вставки). Я рекомендую использовать правильные имена переменных (чтобы избежать таких ошибок) и использовать дженерики.

    Map<String, String> first = new HashMap<String, String>();
    first.put("f"+"oo", "bar"+"bar");
    first.put("fo"+"o", "bar"+"bar");

    Map second = new HashMap();
    second.put("f"+"oo", "bar"+"bar");
    second.put("fo"+"o", "bar"+"bar");

    System.out.println("equals: " + first.equals(second));

конкатенация строк не имеет никакого эффекта, потому что это будет сделано во время компиляции.


@paweloque Для сравнения двух объектов карты в java вы можете добавить ключи карты в список, а с этими 2 списками вы можете использовать методы retainAll() и removeAll () и добавить их в другой общий список ключей и другой список ключей. Используя ключи общего списка и другого списка, вы можете перебирать карту, используя equals вы можете сравнить карты.

приведенный ниже код даст такой вывод: Перед {zoo=варвар, foo=варвар} После {zoo=варвар, foo=варвар} Равный: Перед- Барбар после - Барбар Равный: до-варвар после-варвар

package com.demo.compareExample

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;

public class Demo 
{
    public static void main(String[] args) 
    {
        Map<String, String> beforeMap = new HashMap<String, String>();
        beforeMap.put("foo", "bar"+"bar");
        beforeMap.put("zoo", "bar"+"bar");

        Map<String, String> afterMap = new HashMap<String, String>();
        afterMap.put(new String("foo"), "bar"+"bar");
        afterMap.put(new String("zoo"), "bar"+"bar");

        System.out.println("Before "+beforeMap);
        System.out.println("After "+afterMap);

        List<String> beforeList = getAllKeys(beforeMap);

        List<String> afterList = getAllKeys(afterMap);

        List<String> commonList1 = beforeList;
        List<String> commonList2 = afterList;
        List<String> diffList1 = getAllKeys(beforeMap);
        List<String> diffList2 = getAllKeys(afterMap);

        commonList1.retainAll(afterList);
        commonList2.retainAll(beforeList);

        diffList1.removeAll(commonList1);
        diffList2.removeAll(commonList2);

        if(commonList1!=null & commonList2!=null) // athough both the size are same
        {
            for (int i = 0; i < commonList1.size(); i++) 
            {
                if ((beforeMap.get(commonList1.get(i))).equals(afterMap.get(commonList1.get(i)))) 
                {
                    System.out.println("Equal: Before- "+ beforeMap.get(commonList1.get(i))+" After- "+afterMap.get(commonList1.get(i)));
                }
                else
                {
                    System.out.println("Unequal: Before- "+ beforeMap.get(commonList1.get(i))+" After- "+afterMap.get(commonList1.get(i)));
                }
            }
        }
        if (CollectionUtils.isNotEmpty(diffList1)) 
        {
            for (int i = 0; i < diffList1.size(); i++) 
            {
                System.out.println("Values present only in before map: "+beforeMap.get(diffList1.get(i)));
            }
        }
        if (CollectionUtils.isNotEmpty(diffList2)) 
        {
            for (int i = 0; i < diffList2.size(); i++) 
            {
                System.out.println("Values present only in after map: "+afterMap.get(diffList2.get(i)));
            }
        }
    }

    /**getAllKeys API adds the keys of the map to a list */
    private static List<String> getAllKeys(Map<String, String> map1)
    {
        List<String> key = new ArrayList<String>();
        if (map1 != null) 
        {
            Iterator<String> mapIterator = map1.keySet().iterator();
            while (mapIterator.hasNext()) 
            {
                key.add(mapIterator.next());
            }
        }
        return key;
    }
}


public boolean equalMaps(Map<?, ?> map1, Map<?, ?>map2) {

    if (map1==null || map2==null || map1.size() != map2.size()) {
        return false;
    }

    for (Object key: map1.keySet()) {
        if (!map1.get(key).equals(map2.get(key))) {
            return false;
        }
    }
    return true;
}

Если вы хотите сравнить две карты, то ниже код может помочь вам

(new TreeMap<String, Object>(map1).toString().hashCode()) == new TreeMap<String, Object>(map2).toString().hashCode()