your programing

Java에서 equals 및 hashCode를 재정의 할 때 고려해야 할 문제는 무엇입니까?

lovepro 2020. 10. 2. 23:02
반응형

Java에서 equals 및 hashCode를 재정의 할 때 고려해야 할 문제는 무엇입니까?


오버라이드 (override) 할 때 어떤 문제 / 함정은 고려되어야 equals하고 hashCode?


이론 (언어 변호사 및 수학적으로 기울이는 사람) :

equals()( javadoc ) 등가 관계를 정의해야합니다 ( 반사적 , 대칭 적 , 전 이적 이어야 함 ). 또한 일관성 이 있어야합니다 (객체가 수정되지 않은 경우 동일한 값을 계속 반환해야 함). 또한 o.equals(null)항상 false를 반환해야합니다.

hashCode()( javadoc ) 또한 일관성이 있어야합니다 (객체가에서 수정되지 않은 equals()경우 동일한 값을 계속 반환해야 함).

두 방법 관계 는 다음과 같습니다.

a.equals(b)그때 마다 a.hashCode()와 동일해야합니다 b.hashCode().

실제로:

하나를 재정의하면 다른 하나를 재정의해야합니다.

당신이 계산에 사용하는 필드의 동일한 세트를 사용하여 equals()계산에 hashCode().

Apache Commons Lang 라이브러리 의 우수한 도우미 클래스 EqualsBuilderHashCodeBuilder 를 사용합니다 . 예 :

public class Person {
    private String name;
    private int age;
    // ...

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
            // if deriving: appendSuper(super.hashCode()).
            append(name).
            append(age).
            toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
       if (!(obj instanceof Person))
            return false;
        if (obj == this)
            return true;

        Person rhs = (Person) obj;
        return new EqualsBuilder().
            // if deriving: appendSuper(super.equals(obj)).
            append(name, rhs.name).
            append(age, rhs.age).
            isEquals();
    }
}

또한 기억하십시오 :

HashSet , LinkedHashSet , HashMap , Hashtable 또는 WeakHashMap 과 같은 해시 기반 컬렉션 또는 Map을 사용하는 경우 컬렉션에 넣은 키 개체의 hashCode ()가 개체가 컬렉션에있는 동안 변경되지 않도록해야합니다. 이를 보장하는 방탄 방법은 키를 변경 불가능하게 만드는 것이며, 다른 이점도 있습니다.


Hibernate와 같은 ORM (Object-Relationship Mapper)을 사용하여 지속되는 클래스를 다루고 있다면 이것이 이미 비합리적으로 복잡하다고 생각하지 않았다면 주목할 가치가있는 몇 가지 문제가 있습니다!

지연로드 된 객체는 하위 클래스입니다.

개체가 ORM을 사용하여 유지되는 경우 대부분의 경우 데이터 저장소에서 개체를 너무 일찍로드하지 않도록 동적 프록시를 처리하게됩니다. 이러한 프록시는 자신의 클래스의 하위 클래스로 구현됩니다. 이것은 this.getClass() == o.getClass()반환 될 것임을 의미합니다 false. 예를 들면 :

Person saved = new Person("John Doe");
Long key = dao.save(saved);
dao.flush();
Person retrieved = dao.retrieve(key);
saved.getClass().equals(retrieved.getClass()); // Will return false if Person is loaded lazy

ORM을 다루는 경우 o instanceof Person올바르게 작동하는 유일한 방법은 사용 입니다.

지연로드 된 개체에는 null 필드가 있습니다.

ORM은 일반적으로 게터를 사용하여 지연로드 된 객체를 강제로로드합니다. 이 수단 person.name이 될 것입니다 null경우 person,로드 게으른 경우에도 person.getName()강제로로드 및 반환 "홍길동". 내 경험상, 이것은 hashCode()에서 더 자주 발생 equals()합니다.

ORM 나왔습니다 거래 당신이 경우, 항상에서 게터, 결코 필드 참조를 사용할 수 있는지 확인 hashCode()하고 equals().

개체를 저장하면 상태가 변경됩니다.

영구 객체는 종종 id필드를 사용하여 객체 의 키를 보유합니다. 이 필드는 개체가 처음 저장 될 때 자동으로 업데이트됩니다. 에서 id 필드를 사용하지 마십시오 hashCode(). 그러나 equals().

내가 자주 사용하는 패턴은

if (this.getId() == null) {
    return this == other;
}
else {
    return this.getId().equals(other.getId());
}

그러나 : 당신은 포함 할 수 없습니다 getId()에서 hashCode(). 그렇게하면 객체가 지속될 때 hashCode변경됩니다. 개체가에있는 HashSet경우 다시는 찾을 수 없습니다.

Person에서는 getName()for hashCodeand getId()plus getName()(편집증 전용)를 equals(). 에 대해 "충돌"위험이 hashCode()있어도 괜찮지 만에는 절대로 안됩니다 equals().

hashCode() 변경되지 않는 속성 하위 집합을 사용해야합니다. equals()


에 대한 설명 obj.getClass() != getClass().

이 진술은 equals()상속이 비우호적 인 결과입니다 . JLS (Java 언어 사양)를 지정하면 그 A.equals(B) == true다음은 B.equals(A)또한 반환해야합니다 true. 오버라이드 equals()(및 동작 변경)하는 클래스를 상속하는 문을 생략하면 이 사양이 깨집니다.

명령문이 생략 될 때 발생하는 다음 예를 고려하십시오.

    class A {
      int field1;

      A(int field1) {
        this.field1 = field1;
      }

      public boolean equals(Object other) {
        return (other != null && other instanceof A && ((A) other).field1 == field1);
      }
    }

    class B extends A {
        int field2;

        B(int field1, int field2) {
            super(field1);
            this.field2 = field2;
        }

        public boolean equals(Object other) {
            return (other != null && other instanceof B && ((B)other).field2 == field2 && super.equals(other));
        }
    }    

이렇게 new A(1).equals(new A(1))또한, new B(1,1).equals(new B(1,1))예상대로, 사실 알려주지 발생합니다.

이것은 모두 매우 좋아 보이지만 두 클래스를 모두 사용하려고하면 어떻게되는지보십시오.

A a = new A(1);
B b = new B(1,1);
a.equals(b) == true;
b.equals(a) == false;

분명히 이것은 잘못된 것입니다.

대칭 조건을 보장하려는 경우. a = b if b = a 및 Liskov 대체 원칙 super.equals(other)B인스턴스 의 경우 뿐만 아니라 A들어 확인 합니다.

if (other instanceof B )
   return (other != null && ((B)other).field2 == field2 && super.equals(other)); 
if (other instanceof A) return super.equals(other); 
   else return false;

다음을 출력합니다.

a.equals(b) == true;
b.equals(a) == true;

여기서 a가의 참조가 아니면 B클래스의 참조 일 수 있습니다 A(확장하기 때문에),이 경우 super.equals() 에도 .


상속 친화적 인 구현을 위해 Tal Cohen의 솔루션 인 equals () 메서드를 올바르게 구현하는 방법을 확인하십시오.

요약:

그의 저서 Effective Java Programming Language Guide (Addison-Wesley, 2001)에서 Joshua Bloch는 "동등 계약을 유지하면서 인스턴스화 가능한 클래스를 확장하고 측면을 추가 할 수있는 방법은 없습니다."라고 주장합니다. 탈은 동의하지 않습니다.

그의 해결책은 두 가지 방법으로 다른 비대칭 blindlyEquals ()를 호출하여 equals ()를 구현하는 것입니다. blindlyEquals ()는 서브 클래스에 의해 재정의되고 equals ()는 상속되며 재정의되지 않습니다.

예:

class Point {
    private int x;
    private int y;
    protected boolean blindlyEquals(Object o) {
        if (!(o instanceof Point))
            return false;
        Point p = (Point)o;
        return (p.x == this.x && p.y == this.y);
    }
    public boolean equals(Object o) {
        return (this.blindlyEquals(o) && o.blindlyEquals(this));
    }
}

class ColorPoint extends Point {
    private Color c;
    protected boolean blindlyEquals(Object o) {
        if (!(o instanceof ColorPoint))
            return false;
        ColorPoint cp = (ColorPoint)o;
        return (super.blindlyEquals(cp) && 
        cp.color == this.color);
    }
}

Liskov Substitution Principle 을 충족 하려면 equals ()가 상속 계층 구조에서 작동해야합니다 .


구아바 라이브러리를 추천하는 사람이 없다는 사실에 여전히 놀랐습니다.

 //Sample taken from a current working project of mine just to illustrate the idea

    @Override
    public int hashCode(){
        return Objects.hashCode(this.getDate(), this.datePattern);
    }

    @Override
    public boolean equals(Object obj){
        if ( ! obj instanceof DateAndPattern ) {
            return false;
        }
        return Objects.equal(((DateAndPattern)obj).getDate(), this.getDate())
                && Objects.equal(((DateAndPattern)obj).getDate(), this.getDatePattern());
    }

수퍼 클래스에는 java.lang.Object라는 두 가지 메소드가 있습니다. 사용자 지정 개체로 재정의해야합니다.

public boolean equals(Object obj)
public int hashCode()

Equal objects must produce the same hash code as long as they are equal, however unequal objects need not produce distinct hash codes.

public class Test
{
    private int num;
    private String data;
    public boolean equals(Object obj)
    {
        if(this == obj)
            return true;
        if((obj == null) || (obj.getClass() != this.getClass()))
            return false;
        // object must be Test at this point
        Test test = (Test)obj;
        return num == test.num &&
        (data == test.data || (data != null && data.equals(test.data)));
    }

    public int hashCode()
    {
        int hash = 7;
        hash = 31 * hash + num;
        hash = 31 * hash + (null == data ? 0 : data.hashCode());
        return hash;
    }

    // other methods
}

If you want get more, please check this link as http://www.javaranch.com/journal/2002/10/equalhash.html

This is another example, http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html

Have Fun! @.@


There are a couple of ways to do your check for class equality before checking member equality, and I think both are useful in the right circumstances.

  1. Use the instanceof operator.
  2. Use this.getClass().equals(that.getClass()).

I use #1 in a final equals implementation, or when implementing an interface that prescribes an algorithm for equals (like the java.util collection interfaces—the right way to check with with (obj instanceof Set) or whatever interface you're implementing). It's generally a bad choice when equals can be overridden because that breaks the symmetry property.

Option #2 allows the class to be safely extended without overriding equals or breaking symmetry.

If your class is also Comparable, the equals and compareTo methods should be consistent too. Here's a template for the equals method in a Comparable class:

final class MyClass implements Comparable<MyClass>
{


  @Override
  public boolean equals(Object obj)
  {
    /* If compareTo and equals aren't final, we should check with getClass instead. */
    if (!(obj instanceof MyClass)) 
      return false;
    return compareTo((MyClass) obj) == 0;
  }

}

For equals, look into Secrets of Equals by Angelika Langer. I love it very much. She's also a great FAQ about Generics in Java. View her other articles here (scroll down to "Core Java"), where she also goes on with Part-2 and "mixed type comparison". Have fun reading them!


equals() method is used to determine the equality of two objects.

as int value of 10 is always equal to 10. But this equals() method is about equality of two objects. When we say object, it will have properties. To decide about equality those properties are considered. It is not necessary that all properties must be taken into account to determine the equality and with respect to the class definition and context it can be decided. Then the equals() method can be overridden.

we should always override hashCode() method whenever we override equals() method. If not, what will happen? If we use hashtables in our application, it will not behave as expected. As the hashCode is used in determining the equality of values stored, it will not return the right corresponding value for a key.

Default implementation given is hashCode() method in Object class uses the internal address of the object and converts it into integer and returns it.

public class Tiger {
  private String color;
  private String stripePattern;
  private int height;

  @Override
  public boolean equals(Object object) {
    boolean result = false;
    if (object == null || object.getClass() != getClass()) {
      result = false;
    } else {
      Tiger tiger = (Tiger) object;
      if (this.color == tiger.getColor()
          && this.stripePattern == tiger.getStripePattern()) {
        result = true;
      }
    }
    return result;
  }

  // just omitted null checks
  @Override
  public int hashCode() {
    int hash = 3;
    hash = 7 * hash + this.color.hashCode();
    hash = 7 * hash + this.stripePattern.hashCode();
    return hash;
  }

  public static void main(String args[]) {
    Tiger bengalTiger1 = new Tiger("Yellow", "Dense", 3);
    Tiger bengalTiger2 = new Tiger("Yellow", "Dense", 2);
    Tiger siberianTiger = new Tiger("White", "Sparse", 4);
    System.out.println("bengalTiger1 and bengalTiger2: "
        + bengalTiger1.equals(bengalTiger2));
    System.out.println("bengalTiger1 and siberianTiger: "
        + bengalTiger1.equals(siberianTiger));

    System.out.println("bengalTiger1 hashCode: " + bengalTiger1.hashCode());
    System.out.println("bengalTiger2 hashCode: " + bengalTiger2.hashCode());
    System.out.println("siberianTiger hashCode: "
        + siberianTiger.hashCode());
  }

  public String getColor() {
    return color;
  }

  public String getStripePattern() {
    return stripePattern;
  }

  public Tiger(String color, String stripePattern, int height) {
    this.color = color;
    this.stripePattern = stripePattern;
    this.height = height;

  }
}

Example Code Output:

bengalTiger1 and bengalTiger2: true 
bengalTiger1 and siberianTiger: false 
bengalTiger1 hashCode: 1398212510 
bengalTiger2 hashCode: 1398212510 
siberianTiger hashCode: –1227465966

Logically we have:

a.getClass().equals(b.getClass()) && a.equals(b)a.hashCode() == b.hashCode()

But not vice-versa!


One gotcha I have found is where two objects contain references to each other (one example being a parent/child relationship with a convenience method on the parent to get all children).
These sorts of things are fairly common when doing Hibernate mappings for example.

If you include both ends of the relationship in your hashCode or equals tests it's possible to get into a recursive loop which ends in a StackOverflowException.
The simplest solution is to not include the getChildren collection in the methods.

참고URL : https://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java

반응형