source

Java에서 equals 및 hashCode를 덮어쓸 때 고려해야 할 문제는 무엇입니까?

bestscript 2022. 11. 1. 21:43

Java에서 equals 및 hashCode를 덮어쓸 때 고려해야 할 문제는 무엇입니까?

"/"를 때 해야 할 문제equals ★★★★★★★★★★★★★★★★★」hashCode

이론(언어변호사 및 수학적 성향자용):

equals()(hypadoc)는 동등성 관계를 정의해야 합니다(반사적, 대칭적, 전이적이어야 함).또한 일관성이 있어야 합니다(개체가 수정되지 않은 경우 계속 같은 값을 반환해야 합니다).더 나아가,o.equals(null)거짓

hashCode()(오브젝트가 다음 관점에서 수정되지 않은 경우) 또한 일관성이 있어야 합니다.equals() , 이 값을 반환해야 합니다.

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

★★★★★★★★★★★★★★★★★★시a.equals(b) , , , 「 」a.hashCode()must must must must must must must must must must must must must와 같아야 합니다.와.b.hashCode().

실제:

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

에서 사용하는 것과 동일한 합니다.equals()hashCode().

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

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 등의 해시 기반 컬렉션 또는 맵을 사용할 경우 컬렉션에 추가하는 주요 개체의 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 되다nullperson피우다person.getName()신원미상의내 경험상, 이것은 더 자주 발생한다.hashCode() ★★★★★★★★★★★★★★★★★」equals().

있는 getter를 해 주세요.또, 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'먹다'는 뜻이죠.

의 마마 my에서는Person를 들어, 나는 '나'나 '나'를 할 것이다.getName()★★★★★★에hashCode ★★★★★★★★★★★★★★★★★」getId() ★★★getName()일 뿐equals()에 대해 의 위험이 이치노hashCode() 안 안 돼요.equals().

hashCode() 、 [ Non-changing of ](되지 않은 )을 .equals()

「 」에 obj.getClass() != getClass().

이 스테이트먼트는, 다음의 결과입니다.equals()유전적으로 불친절하다. specification에서는, JLS(Java 「Java」)가 「JLS」라고 지정되어 있습니다.A.equals(B) == trueB.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;

분명히 이건 잘못된 거야.

b인 경우 a b=인 경우 a=b, b=a인 경우 a=b를 호출한다.super.equals(other)B들지만, 그 에 스instanceinstance after after after after after after after after after after after after after after after after를 체크해 주세요.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의 솔루션인 How Do I Regorly Implement the equals() 메서드를 참조하십시오.

요약:.

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

그의 솔루션은 비대칭적인 다른 Equals()를 양방향으로 호출하여 equals()를 구현하는 것입니다.windless Equals()는 서브클래스에 의해 덮어쓰며 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 대체 원칙을 충족하려면 상속 계층 간에 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이라는2개의 메서드가 있습니다.개체. 사용자 지정 개체로 재정의해야 합니다.

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

동일한 개체는 동일한 해시 코드를 생성해야 합니다. 단, 동일한 개체는 고유한 해시 코드를 생성할 필요가 없습니다.

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
}

자세한 내용은 http://www.javaranch.com/journal/2002/10/equalhash.html에서 확인하시기 바랍니다.

이것은 http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html의 또 다른 예입니다.

즐거운 시간 되세요! @@

구성원 평등을 확인하기 전에 계급 평등을 확인하는 몇 가지 방법이 있는데, 저는 둘 다 적절한 상황에서 유용하다고 생각합니다.

  1. 하다를 사용하세요.instanceof환입니니다다
  2. this.getClass().equals(that.getClass()).

1번을 씁니다.final실장 Equals 는 경우(를 들어 Equals " " Equals " " Equals " 등).java.util interfaces: 를 를하는 올바른 방법: " " " " " " 。(obj instanceof Set)또는, 실장하고 있는 인터페이스에도 대응합니다).대칭 특성이 깨지기 때문에 동등성이 무시될 수 있는 경우에는 일반적으로 잘못된 선택입니다.

옵션 #2를 사용하면 대칭을 무시하거나 재정의하지 않고 클래스를 안전하게 확장할 수 있습니다.

의 도 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.Comparable , . . . . . . . .equals ★★★★★★★★★★★★★★★★★」compareTo방법도 일관성이 있어야 합니다.이 있습니다.Comparable 링크:

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;
  }

}

평등에 대해서는 안젤리카 랭거의 평등 비밀대해 알아보세요.나는 그것을 매우 좋아한다.JavaGenerics에 관한 FAQ이기도 합니다.여기서 다른 기사('Core Java'까지 스크롤)를 보고 Part-2와 '혼재형 비교'에 대해서도 설명합니다.재미있게 읽어보세요!

equals() 메서드는 두 개체의 동일성을 판별하기 위해 사용됩니다.

int 값 10은 항상 10과 같기 때문입니다.그러나 이 equals() 메서드는 두 객체의 동등성에 관한 것입니다.오브젝트라고 하면 그 오브젝트는 속성을 갖게 됩니다.평등을 결정하기 위해서는 이러한 특성이 고려됩니다.동등성을 판단하기 위해 모든 속성을 고려할 필요는 없으며 클래스 정의와 맥락에 관해 결정될 수 있다.그러면 equals() 메서드를 덮어쓸 수 있습니다.

equals() 메서드를 덮어쓸 때는 항상 hashCode() 메서드를 덮어써야 합니다.그렇지 않으면 어떻게 됩니까?응용 프로그램에서 해시 테이블을 사용하면 예상대로 작동하지 않습니다.hashCode는 저장된 값의 동일성을 판별할 때 사용되므로 키에 대한 적절한 값은 반환되지 않습니다.

오브젝트 클래스의 hashCode() 메서드는 오브젝트의 내부 주소를 사용하여 이를 정수로 변환하여 반환합니다.

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;

  }
}

코드 출력 예:

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

논리적으로 다음과 같은 것이 있습니다.

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

하지만 그 반대는 아니야!

제가 찾은 한 가지 예는 두 개의 객체가 서로 참조를 포함하는 것입니다(예를 들어 모든 자녀를 취득하기 위한 부모의 편리한 방법과의 부모/자녀 관계).
예를 들어, 이러한 종류의 작업은 휴지 상태 매핑을 수행할 때 매우 일반적입니다.

할 수 ★★★★★★ 。
가장 간단한 해결책은 getChildren 컬렉션을 메서드에 포함하지 않는 것입니다.

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