2016-04-29 3 views
3

Я хотел бы проверить равенство двух LinkedHashMaps в Java.Как проверить равенство LinkedHashMaps в Java - также учитывается порядок вставки?

equals() -method находится в AbstractMap и проверяет, есть ли в этом списке только один и тот же ключ и значение. Таким образом, порядок вставки не проверяется:

package com.stackoverflow.tests; 

import java.util.LinkedHashMap; 

public class LinkedHashMapEqualsTest { 

    public static void main(String[] args) { 
    LinkedHashMap<String, String> lhm1 = new LinkedHashMap<String, String>(); 
    lhm1.put("A", "1"); 
    lhm1.put("B", "2"); 
    lhm1.put("C", "3"); 
    LinkedHashMap<String, String> lhm2 = new LinkedHashMap<String, String>(); 
    lhm2.put("A", "1"); 
    lhm2.put("B", "2"); 
    lhm2.put("C", "3"); 
    LinkedHashMap<String, String> lhm3 = new LinkedHashMap<String, String>(); 
    lhm3.put("A", "1"); 
    lhm3.put("C", "3"); 
    lhm3.put("B", "2"); 
    LinkedHashMap<String, String> lhm4 = new LinkedHashMap<String, String>(); 
    lhm4.put("A", "1"); 
    lhm4.put("B", "2"); 
    LinkedHashMap<String, String> lhm5 = new LinkedHashMap<String, String>(); 
    lhm5.put("A", "2"); 
    lhm5.put("B", "2"); 
    lhm5.put("C", "3"); 

    if(lhm1.equals(lhm1)) { 
     System.out.println("Positive control. - SUCCESS"); 
    } 
    if(lhm1.equals(lhm2)) { 
     System.out.println("lhm1 does equal lhm2; as expected. - SUCCESS"); 
    } 
    if(lhm1.equals(lhm3)) { 
     System.out.println("lhm1 does equal lhm3, although the insert-order is different."); 
    } 
    if(!lhm1.equals(lhm4)) { 
     System.out.println("Negative control 1. - SUCCESS"); 
    } 
    if(!lhm1.equals(lhm5)) { 
     System.out.println("Negative control 2. - SUCCESS"); 
    } 

    } 

} 

Как я могу проверить, если также порядок вставки является одинаковым для обоих сравниваемых списков?

+0

Вы должны переопределить метод equals. По умолчанию используется равная реализация «Абстрактной карты». поэтому вместе с этой реализацией вам нужно выполнить итерацию по карте, чтобы проверить порядок вставки в EntrySet. – SacJn

+3

Если производительность не такая большая, вы можете попробовать и создать списки массивов из наборов записей, например. 'new ArrayList <> (lhm1.entrySet()). equals (новый ArrayList <> (lhm2.entrySet())). Для того чтобы списки были равны, их порядок должен быть одинаковым, а набор записей LinkedHashMap также имеет порядок вставки, так что списки также имеют этот порядок. Конечно, вы можете просто создать метод, который работает непосредственно на наборах записей. – Thomas

ответ

5

я бы, вероятно, не переопределяю equals() из LinkedHashMap но обеспечивает вспомогательный метод, например, как это (вдохновленные AbstractList#equals(...)):

public static <K, V> boolean linkedEquals(LinkedHashMap<K, V> left, LinkedHashMap<K, V> right) { 
    Iterator<Entry<K, V>> leftItr = left.entrySet().iterator(); 
    Iterator<Entry<K, V>> rightItr = right.entrySet().iterator(); 

    while (leftItr.hasNext() && rightItr.hasNext()) { 
    Entry<K, V> leftEntry = leftItr.next(); 
    Entry<K, V> rightEntry = rightItr.next(); 

    //AbstractList does null checks here but for maps we can assume you never get null entries 
    if (! leftEntry.equals(rightEntry)) 
     return false; 
    } 
    return !(leftItr.hasNext() || rightItr.hasNext()); 
} 

Тогда вы используете его как if(linkedEquals(lhm1, lhm3)).

Редактировать:

По желанию, другой способ, который дает более низкую производительность (из-за многократных итераций ненужных), но требует, чтобы писать меньше кода будет преобразовывать наборы входа в списки и сравнить их, например, например:

if(new ArrayList<>(lhm1.entrySet()).equals(new ArrayList<>(lhm3.entrySet())) { ... } 
+0

Вы также опубликуете другое решение. – SacJn

+1

Мне не нравится второе решение почти столько же. a) он всегда выполняет полную итерацию обеих карт, b) он полагается на Map.Entry, правильно реализующий equals (на самом деле обе версии делают это) –

+0

b) нормально, поскольку договор equals() Map.Entry документирован, но a) сосет –

1

Наивный способ сделать это без каких-либо расширения будет использовать toString() выход:

public static <K,V> boolean equalConsideringInsertionOrder(
    Map<K,V> left, Map<K,V> right){ 

    return left.toString().equals(right.toString()); 
} 

Но, как указано в комментариях, эта версия несколько сомнительна. toString() не означает канонический формат, поэтому его не следует использовать таким образом.

Более сложный, эффективный и правильный вариант будет что-то вроде этого:

public static <K, V> boolean equalConsideringInsertionOrder(
     Map<K, V> left, Map<K, V> right) { 

    Iterator<Map.Entry<K, V>> leftIterator = left.entrySet().iterator(); 
    Iterator<Map.Entry<K, V>> rightIterator = right.entrySet().iterator(); 
    while (leftIterator.hasNext() && rightIterator.hasNext()) { 
     Map.Entry<K, V> leftEntry = leftIterator.next(); 
     Map.Entry<K, V> rightEntry = rightIterator.next(); 
     if (!Objects.equals(leftEntry.getKey(), rightEntry.getKey()) 
       || !Objects.equals(leftEntry.getValue(),rightEntry.getValue())) { 
      return false; 
     } 
    } 
    return !leftIterator.hasNext() && !rightIterator.hasNext(); 
} 
+1

Uh uh uh ... Равные представления строк не совпадают с равными объектами. Два неравных объекта очень хорошо разрешены для получения одинаковых результатов в 'toString()'. – mastov

+0

@mastov Теоретически это правда. Но все потомки AbstractMap очень хорошо себя ведут в том, как они реализуют toString –

+1

Я не говорю о карте, я говорю о ключах и значениях. – mastov