2016-06-08 3 views
3

Устали с пытается решить проблему с этим кодом:Ошибка в HashMap/ArrayList или неправильный код?

public class MapTest { 
    static class T{ 
     static class K{} 
    } 
    static Map<List<T.K>, List<String>> map = new HashMap<>(); 
    static List<String> test(List<T.K> list, String s){ 
     List<String> l = map.get(list); 
     if (l == null){ 
      l = new ArrayList<String>(); 
      System.out.println("New value()"); 
      map.put(list, l); 
     } 
     l.add(s); 
     return l;  
    } 
    public static void main(String s[]){   
     ArrayList<T.K> list = new ArrayList<T.K>(); 
     test(list, "TEST"); 
     list.add(new T.K()); 
     List<String> l = test(list, "TEST1"); 
     System.out.println(l.size()); 
    } 
} 

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

New value 
New value 
1 

это что-то случилось с хэш-кодом списка после того, как я вставляю в него значение. Я ожидаю, что «новое значение» появится только один раз, а размер будет равен 2, а не 1. это просто проблема с JVM или что-то более общее? mine one is Oracle JVM 1.8.0_65

+11

В этом примере показано, почему плохая идея использовать изменяемые типы ключей карты. 'list.add (new T.K());' заставляет список генерировать другой хэш-код, поскольку он больше не пуст, поэтому карта больше не может найти существующий экземпляр. Btw: JavaDoc из ['List # hashCode'] (https://docs.oracle.com/javase/7/docs/api/java/util/List.html#hashCode%28%29) очень хорошо объясняет его поведение , – Tom

+5

В целом: стандартные библиотеки java не имеют ошибок. Если у них есть ошибки, они чрезвычайно тонкие. Смысл: можно с большой уверенностью предположить, что библиотека права; и что эти идеи/предположения ... нет. – GhostCat

+0

Когда вы добавили новый T.K в свой список, изменилось значение toString(), поэтому он добавляет другое значение в ваш HashMap. Теперь ключ отличается. Выведите свой список на консоль внутри вашего метода test(), чтобы понять, что я имею в виду. – ManoDestra

ответ

6

Хеш-код объекта списка изменяется, когда вы помещаете в него элемент. Вы можете увидеть, как хэш-код рассчитывается в документации ArrayList.hashCode.

В общем, использование изменяемого объекта в качестве ключа для карты не будет работать хорошо. За Map documentation:

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

Таким образом, когда вы добавляете список на карту во второй раз, карта не считает его «равным» первому списку (поскольку это не соответствует .equals), поэтому он добавляет его снова.

+0

На самом деле я не совсем понимаю идею расчета хэша по элементам, но все в порядке, спасибо. Я действительно устал сегодня;) –

+2

@ Павел Как бы вы отличались List1 и List2, если не считаете свои предметы? – Tom

+0

@Tom, я думал, что equal делает сравнение независимо от hashCodes, а хеш - как обычно Object.hashCode. Я знаю, что ошибался, тогда будет более осторожным. Плохая вещь, которую я уже реорганизовал мой код, а затем Джони упомянул, что IdentityHashMap существует. Обычно я использую объект или строку в качестве ключа, поскольку это более естественно. Но на этот раз мне нужно было хранить пары списка и строки, где строки не были уникальными, поэтому я меняю ключи и значения. Поскольку мне нужен только последовательный доступ, он не страдает от производительности. Но логика была нарушена, потому что я не использовал IdentityHashMap, пока я этого требовал. –

3

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

+0

хорошее примечание, спасибо –

Смежные вопросы