Почему, если реализован метод hashCode, методы равенства также должны быть реализованы в случае ключей в словаре типа данных? - PullRequest
2 голосов
/ 28 октября 2009

Тип данных: словарные ключи

Может кто-нибудь сказать, пожалуйста, важность реализации обоих (hashCode / equals) одновременно. потому что я думаю, что если мы реализуем метод hashCode, то equals собирается сравнить hashCodes и дать нам равенство

Ответы [ 4 ]

5 голосов
/ 28 октября 2009

HashCode не гарантирует уникальность. Например, HashCode принимает 2 ^ 32 значения в большинстве языков. Если у вас есть класс 4-х целых, сколько у вас может быть уникальных состояний / экземпляров этого класса? (2 ^ 32) ^ 4. Это означает, что даже если вы реализуете идеальный хеш-код, у вас все равно будет 2 ^ (32 * 3) коллизии, где пара разных объектов имеет одинаковый хеш-код.

Таким образом, HashCode используется в качестве первого «быстрого» сравнения, чтобы найти объекты, похожие на то, что вы ищете. Как только вы дойдете до группы объектов, на каждом из них проверяется равенство, чтобы определить, есть ли именно то, что вы ищете.

4 голосов
/ 28 октября 2009

То, что хеш-коды равны, не означает, что базовые объекты равны. Количество хеш-кодов ограничено, поэтому возможны коллизии. Вы должны реализовать надежный .Equals(), чтобы вы могли на самом деле проверить на равенство.

3 голосов
/ 28 октября 2009

Проблема в том, что если два объекта имеют одинаковый хеш-код, это не означает, что они равны.

Есть только 2 ^ 32 возможных хеш-кодов (32-разрядные целые числа). Если вы подумаете об этом, вы поймете, что число возможных строк намного намного больше. Поэтому не каждая строка будет иметь уникальный хэш-код.

Кроме того, методы GetHashCode многих классов плохо реализованы.

Например, вот Point.GetHashCode из исходного кода .Net:

public override int GetHashCode() { 
    return x ^ y; 
}

Обратите внимание, что (2, 3) будет иметь тот же хеш-код, что и (3, 2), даже если они не равны. Хотя являются реализациями , которые не демонстрируют такое поведение, они по определению не являются уникальными.

0 голосов
/ 24 апреля 2011

ИМХО, причина для реализации как хеш-кода, так и равных заключается в следующем:

Хеш-таблица обеспечивает быстрый доступ к элементам на основе ключей. Это возможно благодаря его реализации.

Хеш-таблица внутренне использует сегменты для хранения своих значений. Думайте о каждом ведре как о массиве. И есть множество таких ведер. Поэтому он становится двумерным массивом. Хеш-код ключа - это механизм, с помощью которого хеш-таблица может напрямую переходить к индексу корзины, в которой хранится значение.

Например:

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

package com.aneesh.hashtable;  
import java.util.HashMap;  
public class Key {

private String key;

public Key(String key){
    this.key = key;
}


@Override
public int hashCode() {
    return key.hashCode();
}


@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Key other = (Key) obj;
    if (key == null) {
        if (other.key != null)
            return false;
    } else if (!key.equals(other.key))
        return false;
    return true;
}


public static void main(String[] args) {

    HashMap<Key, String> hashMap = new HashMap<Key, String>();
    hashMap.put(new Key("a"), "java");
    hashMap.put(new Key("k"), "Python");

    System.out.println(hashMap.get(new Key("a")));
    System.out.println(hashMap.get(new Key("k")));

}
}

Реализация hashCode класса Key должна просто возвращать hashCode переменной экземпляра 'key', имеющей тип String. Хеш-код для "а" = 97 Хеш-код для "k" = 107 // есть причина, по которой я выбираю эти два ключа, которая скоро станет очевидной.

Когда вы делаете hashMap.put (новый ключ ("a"), "java"); Хеш-таблица должна выяснить, в какую корзину она должна поместить ключ, значение. Код для этого будет

int indexofBucket = key.hashCode() % numberOfBuckets //7, where key is "a"

Таким образом, пара ключ-значение ("a," java ") будет сохранена как первый элемент в 7-м сегменте.

Когда вы делаете hashMap.put (новый ключ ("k"), "python"); индекс ведра снова рассчитывается как indexofBucket = key.hashCode ()% numberOfBuckets // 7, где key = "k"

Это то же самое ведро, ведро на седьмом указателе.

Теперь, когда вы получаете значение по его ключу

hashMap.get(new Key("a"));

хеш-таблица вычислит индекс таким образом:

indexOfBucket = key.hashCode() % numberOfBuckets //7

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

Чтобы увидеть это в действии, закомментируйте реализацию класса равных Key и запустите код. Вы увидите

null  
null 

выводится как вывод, тогда как при внедрении равных вы увидите вывод

"java",  
"python"

Длинное раненое объяснение, но надеюсь, что это поможет

...