← Назад к вопросам

На основании каких данных hashCode вычисляет число

2.0 Middle🔥 171 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью#ORM и Hibernate

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

На основании каких данных hashCode вычисляет число

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

Контракт hashCode()

Java требует соблюдения трёх правил:

  1. Консистентность: если equals() вернёт true, то hashCode() должен вернуть одно и то же число для двух объектов
  2. Стабильность: если объект не изменяется, hashCode() всегда возвращает одно число
  3. Распределение: разные объекты (по equals()) должны возвращать разные hashCode() значения (в идеале)
Основной принцип:
if (obj1.equals(obj2)) → obj1.hashCode() == obj2.hashCode()

На основе чего вычисляется hashCode?

hashCode вычисляется на основе полей объекта, которые учитываются в equals():

public class Person {
    private String name;
    private int age;
    private String email;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person other = (Person) obj;
        // Сравниваем только name и age
        return Objects.equals(name, other.name) && age == other.age;
    }
    
    @Override
    public int hashCode() {
        // hashCode должен вычисляться ТОЛЬКО на основе полей из equals()!
        // email НЕ включаем, потому что его нет в equals()
        return Objects.hash(name, age);
    }
}

Критическое правило: Все поля, используемые в equals(), ДОЛЖНЫ быть использованы в hashCode(). Обратное не требуется.

Методы вычисления hashCode

1. Objects.hash() — рекомендуемый способ (Java 7+)

Это самый простой и надёжный способ:

public class Student {
    private String id;
    private String name;
    private int semester;
    
    @Override
    public int hashCode() {
        return Objects.hash(id, name, semester);
    }
}

// Внутри Objects.hash() используется примерно:
// return Arrays.hashCode(new Object[]{id, name, semester});

Пример вычисления:

Student s1 = new Student("123", "Alice", 3);
int hash = Objects.hash("123", "Alice", 3);
// Вычисляет на основе этих трёх значений

2. Ручной расчёт — классический способ

public class Product {
    private String code;
    private String name;
    private double price;
    
    @Override
    public int hashCode() {
        int result = code.hashCode();              // Начальное значение
        result = 31 * result + name.hashCode();   // Комбинируем
        result = 31 * result + Double.hashCode(price);
        return result;
    }
}

Почему используется 31?

- 31 — нечётное простое число (оптимально для распределения)
- 31 * i == (i << 5) - i (компилятор может оптимизировать)
- Исторически выбрано в Java спецификации

3. IDE генерирует hashCode() автоматически

public class Order {
    private Long id;
    private String customerName;
    private LocalDateTime orderDate;
    
    // Сгенерировано IDE (IntelliJ IDEA, Eclipse)
    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (customerName != null ? customerName.hashCode() : 0);
        result = 31 * result + (orderDate != null ? orderDate.hashCode() : 0);
        return result;
    }
}

Примеры для разных типов данных

1. String hashCode()

public class StringHashCode {
    public static void main(String[] args) {
        String s1 = "Hello";
        String s2 = "Hello";
        String s3 = "World";
        
        // Одинаковые строки имеют одинаковый hashCode
        System.out.println(s1.hashCode());  // Какое-то число (например, 69609650)
        System.out.println(s2.hashCode());  // ТО ЖЕ число 69609650
        System.out.println(s3.hashCode());  // Другое число (например, 83766130)
        
        // String.hashCode() вычисляется как:
        // h = 0;
        // for (int i = 0; i < s.length(); i++) {
        //     h = 31 * h + s.charAt(i);
        // }
    }
}

// "Hello" вычисляется так:
// h = 0
// h = 31*0 + H(72) = 72
// h = 31*72 + e(101) = 2232 + 101 = 2333
// h = 31*2333 + l(108) = 72323 + 108 = 72431
// h = 31*72431 + l(108) = 2245361 + 108 = 2245469
// h = 31*2245469 + o(111) = 69609539 + 111 = 69609650

2. Integer/Long hashCode()

public class NumberHashCode {
    public static void main(String[] args) {
        Integer a = 42;
        Integer b = 42;
        Integer c = 43;
        
        System.out.println(a.hashCode());  // 42 (для Integer просто число)
        System.out.println(b.hashCode());  // 42
        System.out.println(c.hashCode());  // 43
        
        // Для Integer: hashCode() просто возвращает значение
        // public int hashCode() { return value; }
        
        Long d = 42L;
        System.out.println(d.hashCode());  // (int)(42 ^ (42 >>> 32)) = 42
    }
}

3. Boolean hashCode()

Boolean bool1 = true;
Boolean bool2 = true;
Boolean bool3 = false;

System.out.println(bool1.hashCode());  // 1231 (для true)
System.out.println(bool2.hashCode());  // 1231
System.out.println(bool3.hashCode());  // 1237 (для false)

// Ровно два значения: 1231 и 1237

4. Пользовательский класс hashCode()

public class User {
    private int id;
    private String username;
    private String email;
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        // Сравниваем только id и username
        return id == user.id && Objects.equals(username, user.username);
    }
    
    @Override
    public int hashCode() {
        // ТОЛЬКО эти два поля!
        return Objects.hash(id, username);
    }
    
    public static void main(String[] args) {
        User u1 = new User(1, "alice", "alice@example.com");
        User u2 = new User(1, "alice", "alice2@example.com");
        User u3 = new User(2, "bob", "bob@example.com");
        
        // u1 и u2 равны (id и username одинаковые)
        System.out.println(u1.equals(u2));           // true
        System.out.println(u1.hashCode() == u2.hashCode());  // true (ОБЯЗАТЕЛЬНО!)
        
        // u1 и u3 не равны
        System.out.println(u1.equals(u3));           // false
        System.out.println(u1.hashCode() == u3.hashCode());  // false (скорее всего)
    }
}

Типичная ошибка: нарушение контракта

// ❌ ОШИБКА: Поля в equals() и hashCode() не совпадают
public class BadEmployee {
    private int id;
    private String name;
    private String department;
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BadEmployee)) return false;
        BadEmployee emp = (BadEmployee) o;
        return id == emp.id && Objects.equals(name, emp.name);
    }
    
    @Override
    public int hashCode() {
        // ОШИБКА: Не включаем поля из equals(), но включаем department!
        return Objects.hash(department);
    }
}

// Проблема:
BadEmployee e1 = new BadEmployee(1, "Alice", "IT");
BadEmployee e2 = new BadEmployee(1, "Alice", "HR");

System.out.println(e1.equals(e2));           // true (равны по id и name)
System.out.println(e1.hashCode() == e2.hashCode());  // false (ОШИБКА!)

// HashMap будет в замешательстве!

Правильная реализация: полный пример

public class Employee {
    private int id;
    private String name;
    private double salary;
    private String email;  // НЕ участвует в сравнении
    
    public Employee(int id, String name, double salary, String email) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.email = email;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        // Сравниваем только id, name и salary
        return id == employee.id &&
               Double.compare(employee.salary, salary) == 0 &&
               Objects.equals(name, employee.name);
    }
    
    @Override
    public int hashCode() {
        // Вычисляем ТОЛЬКО на основе полей из equals()!
        return Objects.hash(id, name, salary);
    }
    
    public static void main(String[] args) {
        Employee emp1 = new Employee(1, "Alice", 5000, "alice@company.com");
        Employee emp2 = new Employee(1, "Alice", 5000, "alice@other.com");
        Employee emp3 = new Employee(2, "Bob", 6000, "bob@company.com");
        
        // emp1 и emp2 равны (id, name, salary одинаковые)
        // email не учитывается
        System.out.println(emp1.equals(emp2));              // true
        System.out.println(emp1.hashCode() == emp2.hashCode());  // true ✅
        
        // Могут использоваться в HashMap
        HashMap<Employee, String> map = new HashMap<>();
        map.put(emp1, "Значение 1");
        map.put(emp2, "Значение 2");  // Заменит значение для emp1
        
        System.out.println(map.size());  // 1 (не 2!)
    }
}

Таблица hashCode для встроенных типов

ТипКак вычисляетсяПример
StringНа основе символов (31*h + char)"Hello" → 69609650
Integer/LongСамо значение (или часть)42 → 42
BooleanКонстанты: true=1231, false=1237true → 1231
DoubleБитовое представление3.14 → хеш от битов
List/SetНа основе элементов[1,2,3] → хеш от содержимого
HashMapНа основе key-value пар{a=1,b=2} → хеш от пар

Best Practices

Правильно:

  • Используйте Objects.hash() (самый простой способ)
  • Включайте в hashCode() все поля из equals()
  • Проверяйте согласованность equals() и hashCode()
  • Используйте IDE для генерирования
  • Для immutable объектов кешируйте hashCode

Неправильно:

  • Не забывайте переопределять hashCode(), если переопределили equals()
  • Не включайте разные поля в equals() и hashCode()
  • Не вычисляйте hashCode на основе mutable полей (они могут измениться)
  • Не используйте все поля в hashCode(), если в equals() не все

Вывод

hashCode вычисляется на основе полей объекта, конкретно:

  1. Используются ВСЕ поля, которые участвуют в equals()
  2. НЕ используются поля, которых нет в equals()
  3. Может использоваться любой алгоритм (Objects.hash, ручной расчёт, 31 * h + value)
  4. Главное требование — если equals() возвращает true, то hashCode() должен быть идентичным

Это соглашение обеспечивает правильную работу хеш-таблиц (HashMap, HashSet) и других структур данных.

На основании каких данных hashCode вычисляет число | PrepBro