Топ-100Метод hashCode и его переопределение - CodOrbits
LogoCodOrbits

Раздел: Java Core

Раздел охватывает ключевые возможности языка Java: аннотации, исключения, generics, лямбда-выражения, интерфейсы и другие базовые концепции.

Все разделы
Иконка Java Core

Метод hashCode и его переопределение

Last updated: 5 мая 2025 г.

Search Icon

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

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

Это случайное уникальное число, которое генерируется этим методом по умолчанию.

Переопределять его нужно если мы собираемся запихивать в HashSet или HashMap не простые элементы типа char, int, String и т.д., а объекты.

То есть, например, так – set.add(new MyClass(1, 34));.

Вот например мы только что записали в set объект new MyClass(1, 34) и у нас в классе MyClass пока не переопределен hashCode и если мы теперь запишем в этот же set такой же объект new MyClass(1, 34) еще раз вот так – set.add(new MyClass(1, 34));, то в set уже будет ДВА элемента.

А это не должно быть так! Так как мы помним, что ни в HashSet, ни в HashMap одинаковые ключи храниться не должны.

Почему же если мы записываем в hashset идентичные объекты, как ключи, то hashset рассматривает их как разные ключи?

Реализация метода hashCode по умолчанию генерирует разные ключи всем объектам ДАЖЕ ЕСЛИ ОНИ ИДЕНТИЧНЫ по своему содержанию.

То есть если мы создаем объект new MyClass(1, 34) в первый раз, то у него будет свой hashCode, когда мы создаем new MyClass(1, 34) второй раз, у него уже будет другой hashCode.

Каждый объект имеет свой hashCode. И hashset добавляет объекты в себя по этому hashCode.

Если hashCode у добавляемых объектов разный, значит эти объекты с наибольшей вероятностью попадут в разные linkedlist-ы в 16 linkedlist-ах, если же они одинаковые, то объекты будут попадать в один и тот же linkedlist.

Как же нам переопределить hashCode, чтобы идентичные объекты всегда записывались в один и тот же linkedlist?

Нам нужно переопределить hashCode так, чтобы он возвращал данные объекта представленные одним восьмибитным числом.

То есть все значения полей объекта нам нужно каким-то образом скомпановать в одно восьмибитное число, которое будет возвращать hashCode.

И теперь hashCode всех идентичных объектов, например, new MyClass(1, 34) всегда будет возвращать один и тот же hashCode, так как он является скомпонованными полями объекта, а поля у идентичных объектов new MyClass(1, 34) одинаковые – 1 и 34.

Но на этом еще не всё. Если объекты одинаковы по hashCode, это только значит, что они попадут в один и тот же самый linkedlist, это еще не обязательно значит, что они одинаковы полностью.

HashSet еще будет сравнивать добавляемый в него объект со всеми уже присутствующими в hashset элементами методом equals и если он НЕ найдет там методом equals идентичный элемент, но при этом объект с таким hashCode уже там присутствует, то в hashset всё равно добавиться этот добавляемый объект и в итоге в нем будет два элемента с одинаковыми hashCode.

Search Icon

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


Переопределение HashCode

Пример программы:

1import java.util.*;
2
3class MyClass implements Cloneable {
4    int myA;
5    SomeClass myB;
6
7    MyClass(int myA, SomeClass myB){
8        this.myA = myA;
9        this.myB = myB;
10    }
11
12    // hashCode – данные класса представлены одним восьмибитным числом. 
13    // По умолчанию, если не переопределять, это случайное уникальное число.
14    @Override
15    public int hashCode() {
16        System.out.println("HashCode is called: " + this);
17        // Это стандартное переопределение. Не заморачивайтесь почему 31, 
18        // почему умножение. нам лишь важно в result
19        // добавить все поля класса, так как result это и есть 
20        // это самое восьмибитное число, состоящее из полей класса.
21        final int prime = 31;
22        int result = 1;
23        // Числовые переменные просто добавляем.
24        result = prime * result + myA;
25        // У полей объектов вызываем hashCode.
26        // Метод hashCode в классе этих объектов 
27        // тоже должен быть определен таким же образом.
28        result = prime * result + myB.hashCode();
29        return result;
30    }
31
32    // equals обязательно тоже переопределяем, как уже было сказано
33    @Override
34    public boolean equals(Object obj) {
35        System.out.println("Equals is called:" + this + " : " + obj);
36        if (this == obj)
37            return true;
38        if (obj == null)
39            return false;
40        if (getClass() != obj.getClass())
41            return false;
42        MyClass other = (MyClass) obj;
43        if (myA != other.myA)
44            return false;
45        if (!myB.equals(other.myB))
46            return false;
47        return true;
48    }
49
50    @Override
51    public String toString() {
52        return "MyClass{" + "myA=" + myA + ", myB='" + myB + ‘\" +};
53    }
54
55    @Override
56    public SomeClass clone() throws CloneNotSupportedException {
57        Object obj = super.clone();
58        SomeClass someClass = (SomeClass) obj;
59        return someClass;
60    }
61}
62
63class SomeClass implements Cloneable {
64    int someVar;
65    SomeClass(int someVar){
66        this.someVar = someVar;
67    }
68
69    // Здесь тоже переопределены hashCode и equals
70
71    @Override
72    public int hashCode() {
73        System.out.println("HashCode is called:" + this);
74        final int prime = 31;
75        int result = 1;
76        result = prime * result + someVar;
77        return result;
78    }
79
80    @Override
81    public boolean equals(Object obj) {
82        System.out.println("Equals is called:" + this + " : " + obj);
83        if (this == obj)
84            return true;
85        if (obj == null)
86            return false;
87        if (getClass() != obj.getClass())
88            return false;
89        SomeClass other = (SomeClass) obj;
90        if (someVar != other.someVar)
91            return false;
92        return true;
93    }
94
95    @Override
96    public SomeClass clone() throws CloneNotSupportedException {
97        Object obj = super.clone();
98        SomeClass someclass = (SomeClass)obj;
99        return someclass;
100    }
101
102    public String toString(){
103        return "SomeClass{" + "someVar=" + someVar +};
104    }
105}
106
107public class hashCodeLesson {
108    public static void main(String[] args)
109        throws CloneNotSupportedException {
110        Set< MyClass > set = new HashSet<>();
111        set.add(new MyClass(1, new SomeClass(34)));
112        set.add(new MyClass(1, new SomeClass(36)));
113        set.add(new MyClass(1, new SomeClass(34)));
114        set.add(new MyClass(2, new SomeClass(26)));
115        set.add(new MyClass(3, new SomeClass(75)));
116
117        System.out.println("SIZE:" + set.size());
118        
119        // По результатам можно увидеть, что размер коллекции 4,
120        // так как для 1 и 3 элементов хеш-коды одинаковые,
121        // и equals сравнил поля, которые оказались одинаковыми.
122        // Если бы hashCode не был переопределен, элементов было бы 5.
123    }
124}

Вывод:


Последовательность добавления элементов в HashSet

Также стоит упомянуть некоторые детали последовательности добавления элементов в HashSet.

При добавлении ключа в HashSet и расчета его hashCode происходит сравнение этого hashCode с hashCode каждого элемента в HashSet, и если hashCode очередного добавляемого объекта отличается от всех остальных уже присутствующих в коллекции, то ключ добавляется СРАЗУ, без сравнения по equals.

Если же такой же hashCode нашелся, то происходит сравнение по equals, и если этим методом не найдет такого же элемента, то произойдет добавление.

В этом уроке был приведен пример стандартного переопределения hashCode.

Search Icon

Таким вот образом его нужно переопределять почти всегда когда вы работаете с hash коллекциями.


Следующие уроки

Коллекции. Интерфейс List

10
мин.

Similar Articles Icon
Divider

Iterator в коллекциях Java

8
мин.

Similar Articles Icon
Divider

Интерфейс Queue в Java

9
мин.

Similar Articles Icon