Хэш-код () против равно ()

У меня ниже эти два класса ..

class Emp //implements Comparable
{
      String name,job;
      public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    public int getSalary() {
        return salary;
    }
    public void setSalary(int salary) {
        this.salary = salary;
    }
    int salary;
      public Emp(String n,String j,int sal)
      {
         name=n;
         job=j;
         salary=sal;
       }
      public void display()
      {
        System.out.println(name+"\t"+job+"\t"+salary);
       }



  public boolean equals(Object o)
      {

         Emp p=(Emp)o;
          return this.name.equals(p.name)&&this.job.equals(p.job) &&this.salary==p.salary;
       }
  /* public int hashCode()
       {
          return name.hashCode()+job.hashCode()+salary;
       }
     */

      /* public int compareTo(Object o)
       {
          Emp e=(Emp)o;
          return this.name.compareTo(e.name);
           //return this.job.compareTo(e.job);
        //   return this.salary-e.salary;

        }*/
} 

а другой ..

import java.util.*;
class EmpHsDemo
{
      public static void main(String arg[])
      {
          HashSet set=new HashSet();
          set.add(new Emp("Ram","Trainer",34000));
          set.add(new Emp("Ram","Trainer",34000));
          set.add(new Emp("Ravi","Administrator",44000));
          set.add(new Emp("Sachin","Programmer",24000));
          set.add(new Emp("Priyanka","Manager",54000));
          set.add(new Emp("Anupam","Programmer",34000));
          set.add(new Emp("Sachin","Team Leader",54000));
          System.out.println("There are "+set.size()+" elements in the set.");
          System.out.println("Content of set are : ");
          Iterator itr=set.iterator();
          while(itr.hasNext())
          {
            Emp e=(Emp)itr.next();
            System.out.print(e.hashCode()+"\t");   
            e.display();
          }   


          System.out.println("**********************************");
        Emp e1=new Emp("Ravi","Administrator",44000);
        System.out.println("Removing following Emp from the set...");
        System.out.print(e1.hashCode()+"\t");
        e1.display();
        set.remove(e1);
        System.out.println("No. of elements after removal "+set.size());
       /* Emp e2=new Emp("Anupam","Programmer",34000);
        System.out.println("Searching following Emp in the set...");
        System.out.print(e2.hashCode()+"\t");
        e2.display();
        System.out.println("Results of searching is : "+set.contains(e2));*/
      }
}

Сейчас я проводил одно исследование, которое

  1. Если я прокомментирую метод hashcode (), а не закомментирую метод equals (), то есть он будет использовать, он позволяет дублировать, поскольку Ram отображается дважды вместе с адресом памяти, я получаю следующий результат.
There are 7 elements in the set.
Content of set are : 
374283533   Priyanka    Manager 54000
1660364311  Ram Trainer 34000
1340465859  Ravi    Administrator   44000
2106235183  Sachin  Programmer  24000
2031692173  Ram Trainer 34000
603737068   Anupam  Programmer  34000
148669801   Sachin  Team Leader 54000
**********************************
Removing following Emp from the set...
1807500377  Ravi    Administrator   44000
No. of elements after removal 7

2. Если я раскомментирую метод hashcode () и метод equals (), я получу этот результат

There are 6 elements in the set.
Content of set are : 
1546622676  Sachin  Team Leader 54000
-302767206  Anupam  Programmer  34000
149315535   Ravi    Administrator   44000
199998062   Sachin  Programmer  24000
1407883922  Priyanka    Manager 54000
597555555   Ram Trainer 34000
**********************************
Removing following Emp from the set...
149315535   Ravi    Administrator   44000
No. of elements after removal 5

3. Если я прокомментирую только метод equals (), а не hashcode (), я получу следующий результат

There are 7 elements in the set.
Content of set are : 
1546622676  Sachin  Team Leader 54000
-302767206  Anupam  Programmer  34000
149315535   Ravi    Administrator   44000
199998062   Sachin  Programmer  24000
1407883922  Priyanka    Manager 54000
597555555   Ram Trainer 34000
597555555   Ram Trainer 34000
**********************************
Removing following Emp from the set...
149315535   Ravi    Administrator   44000
No. of elements after removal 7

Посоветуйте, пожалуйста, какой резон там за три подхода ..!


person user1582269    schedule 07.08.2012    source источник
comment
См. http://stackoverflow.com/questions/410236/how-to-ensure-hashcode-is-consistent-with-equals   -  person Richard Sitze    schedule 07.08.2012


Ответы (5)


У HashSet есть несколько сегментов. Он использует hashCode(), чтобы определить, к какому сегменту принадлежит элемент, а затем внутри этого сегмента он использует equals(), чтобы определить, существует ли элемент в этом сегменте или нет.

В реализациях этих методов по умолчанию hashCode() используется хэш-код системной идентификации, который, вероятно, является его местом в памяти, но уникален тем, что система будет пытаться использовать все "разумно практичные" попытки сделать так, чтобы ни у одного из двух объектов не было одного и того же хэш-кода идентификации системы. , и equals сравнивает ячейки памяти двух объектов. (Обратите внимание, что хэш-код системной идентификации не гарантирует создание уникальных хэшей; скорее, он пытается очень, очень, очень сложно дать вам уникальные хэши. См. Ошибка Java 6321873 для дальнейшего чтения по этому вопросу.)

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

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

if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

Это итерация по каждому объекту в ведре. Их хэши не равны, поэтому он никогда не дойдет до проверки определения, равен ли сам объект предоставленному.

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

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

Также стоит повторить, что ответ duffymo полностью игнорирует ваш вопрос; в 99,9% случаев я бы сказал, что это неправильно, и что мы должны сначала понять масштаб вашей проблемы. Это один из очень немногих случаев, когда правильный игнорирует ваш вопрос. Вы не должны никогда переопределять только один из хэш-кода и равно. Вам следует всегда делать ни то, ни другое, но ни в коем случае не делать только одно.

person corsiKa    schedule 07.08.2012
comment
Ваше второе предложение неверно. Не гарантируется, что хэш-код системной идентификации будет уникальным для каждого объекта. Спецификация не налагает таких ограничений. - person Robin; 07.08.2012
comment
На самом деле это мое третье предложение :-). На самом деле я не знал, что это не указано. Я добавил необходимую информацию. Спасибо! - person corsiKa; 07.08.2012

Не стоит читать всю опубликованную вами информацию.

Джошуа Блох говорит, что hashCode и equals должны быть переопределены вместе. Вот как это сделать, без исключений.

person duffymo    schedule 07.08.2012
comment
И да и нет. С одной стороны, да - это одна из абсолютных вещей в Java. С другой стороны, категорически и решительно нет - вы должны всегда пытаться понять, как что-то работает, до применения, а не только на основании авторитетных утверждений. - person pafau k.; 07.08.2012
comment
Я понимаю и согласен с властями в этом случае. Если вы проголосовали против, я бы сказал, что это неправильно вы, а не я. Я считаю, что автор Java Collections API, который знает, что требует его API, в данном случае достаточно авторитетен. Поскольку OP использует коллекции Java, ему следует следовать рекомендациям. - person duffymo; 07.08.2012
comment
@pafau Я согласен с тем, что мы должны давать объяснения в дополнение к официальным заявлениям. Это выглядит глупо ТАКЖЕ агрессивно, потому что он связал главу «Эффективная Java», в которой объясняются все тонкости того, почему существует догма хэш-кода / равенства. Единственное, чего он не сделал, так это того, что пункт 8 касается конкретно hashcode / equals, но, честно говоря, вся глава важна для всех Java-программистов. - person corsiKa; 07.08.2012

1. Когда 2 объекта равны, они должны иметь same hashcode.

2. Если 2 объекта имеют same hascode,, они должны вызвать equals () друг для друга, чтобы проверить, равны ли они.

3. Если хэш-код совпадает со своим не обязательно, чтобы они оба были равны.

4. Поэтому очень важно, что когда equals() метод переопределен, тогда hashcode() метод также должен быть переопределен.

5. HashSet при взятии объекта в проверяет, где этот объект помещается в HashSet, то есть ли какой-либо объект с таким же хэш-кодом, если есть затем оба помещаются в корзину с same hashcode в качестве метки, затем вызываются equals() друг у друга, чтобы узнать, действительно ли они равны.

person Kumar Vivek Mitra    schedule 07.08.2012

HashSet использует HashMap как внутреннее представление данных. HashSet.add() и HashSet.remove() работают над значением hash (которое вы указываете в своем hashCode() методе. Как только вы закомментируете hashCode(), все ставки будут отключены.

Мое сердце в некоторой степени связано с Даффимо в этом вопросе, но с другой стороны, я почти так же категорически против того, чтобы что-то делать, потому что ваш авторитет сказал вам об этом, не понимая.

person pafau k.    schedule 07.08.2012
comment
На мой взгляд, вы не понимаете, какой здесь авторитет. Контекст - это коллекции Java, и рекомендации по правильной работе этих структур данных исходят от человека, который их разработал. - person duffymo; 07.08.2012

corsiKa ответила правильно. Просто добавляю еще несколько точек, чтобы упростить задачу. @ user1582269, вы используете Set Algo и Hashing - это структура данных. Согласно концепции Set - каждый элемент Set должен быть уникальным. Дублирование элемента не допускается. На каком основании элемент будет уникальным? Для достижения этой уникальности метод equals () должен быть переопределен. т.е. Emp 1 должен отличаться от Emp 2. Теперь вы используете структуру хеш-данных. Хеширование обеспечивает хранение, извлечение и поиск элементов. Чтобы хеширование работало эффективно, необходимо переопределить хеш-функцию, т.е. hashCode (), чтобы не было коллизий.

Для получения дополнительных сведений о хешировании следует прочитать объяснение corsiKa. Поэтому, если вы не переопределяете hashCode (), реализация hashCode () по умолчанию вернет некоторый идентификатор корзины (предположим, что какой-то индекс в массиве), например, например 4. Таким образом, если элемент empX уже сохранен в сегменте с идентификатором 4, а для другого сотрудника empY возвращается тот же хэш-код, то есть 4, тогда будет конфликт между empX и empY.

Надеюсь, это проясняет ваши сомнения.

person Free Coder    schedule 07.08.2012