Ключевые проблемы HashMap - PullRequest
2 голосов
/ 12 июня 2010

Я профилирую некоторый старый код Java, и кажется, что мое кэширование значений с использованием статического HashMap и метода доступа не работает.

Кэширующий код (немного абстрактно):

static HashMap<Key, Value> cache = new HashMap<Key, Value>();

public static Value getValue(Key key){
    System.out.println("cache size="+ cache.size());                
    if (cache.containsKey(key)) {
        System.out.println("cache hit");
        return cache.get(key);
    } else {
        System.out.println("no cache hit");
        Value value = calcValue();
        cache.put(key, value);
        return value;
    }
}

Код профилирования:

for (int i = 0; i < 100; i++)
{ 
    getValue(new Key());
}

Вывод результата:

 cache size=0
 no cache hit
 (..)
 cache size=99
 no cache hit

Выглядело такстандартная ошибка в хэш-коде Key или коде равенства.Однако:

 new Key().hashcode == new Key().hashcode // TRUE
 new Key().equals(new Key()) // TRUE

Что особенно странно, так это то, что cache.put(key, value) просто добавляет другое значение в хэш-карту вместо замены текущего.

Итак, я не совсем понимаю, что происходитЗдесь.Я что-то не так делаю?

edit Хорошо, я вижу, что в реальном коде Key используется в других методах и изменениях, которые поэтому отражаются в hashCodeобъект в HashMap.Может ли это быть причиной такого поведения, что оно пропадает без вести?

Ответы [ 4 ]

4 голосов
/ 12 июня 2010

На правильном @Override из equals/hashCode

Я не уверен, что вы @Override (вы используете аннотацию, верно?) hashCode/equals правильно. Если вы не использовали @Override, возможно, вы определили int hashcode() или boolean equals(Key), ни один из которых не будет выполнять то, что требуется.


по ключевой мутации

Если вы меняете ключи на карте, тогда да, возникнут проблемы. С документация :

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

Вот пример:

Map<List<Integer>,String> map =
    new HashMap<List<Integer>,String>();
List<Integer> theOneKey = new ArrayList<Integer>();
map.put(theOneKey, "theOneValue");

System.out.println(map.containsKey(theOneKey)); // prints "true"
theOneKey.add(42);
System.out.println(map.containsKey(theOneKey)); // prints "false"

Кстати, предпочитайте интерфейсы классам реализации в объявлениях типов. Вот цитата из Effective Java 2nd Edition: Пункт 52: Ссылка на объекты по их интерфейсам

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

В этом случае, если это вообще возможно, вы должны объявить cache просто Map вместо HashMap.

0 голосов
/ 12 июня 2010

Я не уверен, какой у вас класс Key, но (абстрактно аналогично вам) я бы сделал для простой проверки:

Key k1 = new Key();
Key k2 = new Key();
System.out.println("k1 hash:" + k1.hashcode);
System.out.println("k2 hash:" + k2.hashcode);
System.out.println("ks equal:" + k1.equals(k2));
getValue(k1);
getValue(k2);

если это код показывает аномалию - тот же хэш-код, равные ключи, но кеша еще нет - , тогда есть повод для беспокойства (или, лучше, отладки вашего Key класса ;-).То, как вы тестируете, с новыми Key все время, может привести к ключам, которые не обязательно ведут себя одинаково.

0 голосов
/ 12 июня 2010

Глядя на (абстрагированный) код, кажется, все в порядке.Может случиться так, что реальный код не похож на вашу отредактированную версию, и это скорее отражает то, как вы ожидаете, что код будет работать, а не то, что происходит на практике!пожалуйста, сделай это.А пока вот несколько указателей, которые нужно попробовать:

  • После добавления ключа снова используйте точно такой же экземпляр ключа и убедитесь, что он вызывает попадание в кэш.test, убедитесь, что хеш-коды равны, и что объекты равны.
  • Является ли реализация Map действительно HashMap?WeakHashMap будет вести себя так, как вы описываете, когда ключи больше недоступны.
0 голосов
/ 12 июня 2010

Я бы рекомендовал двойную и тройную проверку методов equals и hashCode.Обратите внимание, что это hashCode, а не hashcode.

...