Медовый сок равен и хэш-код
Ха-ха! У меня разборка! Интервьюеры Ali и Meituan задавали мне один и тот же вопрос: вы уверены, что не понимаете?
Сегодня поговорим о чем-то простом, что есть в初级开发及校招
В интервью часто задают вопрос.
-
На что следует обратить внимание, когда ключ HashMap является объектом?
-
Зачем одновременно переопределять методы equals и hashcode?
Даю тебе десять секунд, чтобы подумать, как тебе ответить... ⌚
Неважно, если вы не можете об этом подумать, после прочтения этой статьи я столкнулся с той же проблемой в интервью.送分题
. 📕
Что такое методы equals и hashcode
Мы знаем, что все классы в Java наследуются от класса Object, а класс Object является родительским классом для всех классов. Когда подкласс вызывает метод, если этот метод не был переопределен, ему необходимо найти метод в суперклассе для выполнения.
Класс объекта
public boolean equals(Object obj) {
return (this == obj);
}
public native int hashCode();
В классе Object hashCode — это локальный метод, который просто понимается как获取对象地址
, метод equals сравнивает себя с obj对象地址
равны. Здесь вы должны сначала распознать эти два метода, один — взять адрес, другой — сравнить адрес.
Введите тему ниже...
Что происходит, когда объект является ключом
Ниже приведен небольшой код для демонстрации, выводnull
. Позвольте мне сначала сказать кое-что: здесь мы думаем, что мои a и b являются одним и тем же объектом (ключом) с одинаковыми свойствами, и я могу получить строку hello через hashMap.get(b). Но это имело неприятные последствия.
public class NoHashCodeAndEquals {
public static void main(String[] args) {
Object o = new Object();
HashMap<Demo, String> hashMap = new HashMap<>();
Demo a = new Demo("A");
Demo b = new Demo("A");
hashMap.put(a, "hello");
String s = hashMap.get(b);
System.out.println(s);
}
}
class Demo {
String key;
Demo(String key) {
this.key = key;
}
}
Может быть, каждый увидит это с первого взгляда и почувствует, что проблемы нет, a и b — не один и тот же объект! Адрес точно другой! Останавливаться! О чем ты говоришь, что я только что сказал? 👆 Запутался! 😵 (пс: я сама почти в кругу🤭).
Подожди! ! !
Давайте просто посмотрим на две строки исходного кода HashMap и сразу проснемся после прочтения.
- Получите хэш-код для вычисления нижнего индекса корзины и сохранения элемента.Кажется, в вычислении нижнего индекса нет ничего плохого. правильно! Просто вызовите хэш-код ключа, чтобы вычислить значение индекса.
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Видя метод hashCode, нам все еще не хватает метода equals.Где вызов метода equals() в HashMap?
- метод put (в качестве примера возьмем JDK1.7)
public V put(K key, V value) {
...
int hash = hash(key);
// 确定桶下标
int i = indexFor(hash, table.length);
// 先找出是否已经存在键为 key 的键值对,如果存在的话就更新这个键值对的值为 value
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
...
// 插入新键值对
addEntry(hash, key, value, i)
return null;
}
- метод get (в качестве примера возьмем JDK1.7)
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
Как показано выше: Когда HashMap вызывает метод вставки или получения, необходимо сравнить хэш-код, соответствующий значению (ключу), с хэш-кодом в массиве, Если они равны, метод equals будет использоваться для сравнения значения ключей равны.
В сочетании с кодовым случаем, который я написал выше, давайте воспользуемся моментом и попробуем его снова!
Demo a = new Demo("A");
Demo b = new Demo("A");
Смысл этих двух строк кода мы понимаем, что он определяет два相同含义
(думая, что это один и тот же ключ) ключевой объект, но все знают, что значение метода хэш-кода этих двух ключей различно.
Ключ сравнения в HashMap такой, сначала находим hashcode() ключа, сравниваем, равны ли его значения, а потом сравниваем equals(), если они равны, если они равны, они считаются равными. Если equals() не равно, то они не считаются равными.
- Если переопределен только метод hashcode(), а метод equals() не переопределен, при сравнении equals() фактически вызывается метод в Object, просто чтобы увидеть, являются ли они одним и тем же объектом (т.е. сравнить адреса памяти) .
- Если вы переопределяете только equals() без переопределения метода hashcode(), HashMap будет заблокирован при принятии решения и будет рассматриваться как другой ключ.
Итак, я хочу использовать объект в качестве ключа HashMap,Вы должны переопределить методы hashCode и equals объекта.. Убедитесь, что значение equals также истинно, когда hashCode равен.
- Схема выглядит следующим образом:
Эта задача кажется простой, но многие младшие программисты не могут сказать почему: с одной стороны, они не знакомы с HashMap, а с другой стороны, не до конца понимают смысл этих двух методов.
Наконец, снова резюмируем: храните пользовательские объекты в «ключевой» части HashMap,一定
переписатьequals
иhashCode
метод. Еще два клише!
- Если два объекта == равны, их хэш-коды должны быть равны, и наоборот.
- Если равны два объекта равны, их хэш-коды должны быть равны, и наоборот не обязательно верно.
Как насчет того, чтобы снова выпить его самому~