답변:
equals()
( javadoc )는 동등성 관계를 정의해야합니다 ( 반사적 , 대칭 적 , 전 이적 이어야 함 ). 또한 일관성 이 있어야합니다 (개체를 수정하지 않은 경우 계속 같은 값을 반환해야 함). 또한 o.equals(null)
항상 false를 반환해야합니다.
hashCode()
( javadoc )도 일관성이 있어야합니다 (객체가로 수정되지 않은 경우 equals()
계속 동일한 값을 반환해야 함).
두 방법 의 관계 는 다음과 같습니다.
때마다
a.equals(b)
, 다음a.hashCode()
과 같은 동일해야합니다b.hashCode()
.
하나를 재정의하면 다른 것을 재정의해야합니다.
당신이 계산에 사용하는 필드의 동일한 세트를 사용하여 equals()
계산에 hashCode()
.
Apache Commons Lang 라이브러리 의 탁월한 헬퍼 클래스 EqualsBuilder 및 HashCodeBuilder 를 사용하십시오 . 예를 들면 :
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 과 같은 해시 기반 Collection 또는 Map을 사용 하는 경우, 오브젝트가 콜렉션에있는 동안 콜렉션에 넣은 키 오브젝트의 hashCode ()가 절대 변경되지 않도록하십시오. 이를 보장하는 방탄 방법은 키를 불변으로 만드는 것인데 다른 이점도 있습니다.
instanceof
번째 피연산자가 null 인 경우 false 를 반환 한다는 사실을 고려하면 첫 번째 null 검사는 필요하지 않습니다 (Effective Java 다시).
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
올바르게 사용하는 것이 유일한 방법입니다.
게으른로드 된 오브젝트에는 널 필드가 있습니다.
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()
위해 hashCode
및 getId()
플러스 getName()
에 대한 (단지 편집증에 대한) equals()
. 에 대한 "충돌"의 위험이있는 경우에는 hashCode()
괜찮지 만 결코 괜찮습니다 equals()
.
hashCode()
속성의 변경되지 않는 하위 집합을 사용해야합니다. equals()
Saving an object will change it's state
돼요! hashCode
반환해야 int
하므로 어떻게 사용 getName()
하시겠습니까? 당신의 예를들 수 있습니까hashCode
에 대한 설명 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;
분명히 이것은 잘못된 것입니다.
대칭 조건을 보장하려는 경우. b = a 인 경우 a = b이고 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()
에도 호출 합니다.
ThingWithOptionSetA
A와 동일 할 수 있습니다 Thing
모든 추가 옵션이 기본 값을 가지고 있고, 마찬가지로에 대한 것을 제공 ThingWithOptionSetB
A를 위해, 다음이 가능해야한다 ThingWithOptionSetA
동등한 비교 될 수있는 ThingWithOptionSetB
두 개체의 모든 비 기본 특성은 기본값과 일치하는 경우에만 있지만, 어떻게 테스트하는지 모르겠습니다.
B b2 = new B(1,99)
다음 b.equals(a) == true
과 a.equals(b2) == true
하지만 b.equals(b2) == false
.
상속 친화적 인 구현을 위해서는 Tal Cohen의 솔루션, equals () 메소드를 올바르게 구현하는 방법을 확인하십시오.
요약:
Joshua Bloch는 자신의 저서 Effective Java Programming Language Guide (Addison-Wesley, 2001)에서 "인스턴스 계약을 유지하면서 인스턴스화 가능한 클래스를 확장하고 측면을 추가 할 수있는 방법이 없다"고 주장합니다. 탈이 동의하지 않습니다.
그의 해결책은 두 가지 방법으로 다른 비대칭 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 대체 원칙 을 만족 하려면 equals ()가 상속 계층 구조에서 작동해야합니다 .
if (this.getClass() != o.getClass()) return false
하지만 파생 클래스가 동등하게 수정하려는 경우에만 false를 반환한다는 점에서 융통성이 있습니다. 맞습니까?
아무도 구아바 도서관을 추천하지 않았다는 것에 여전히 놀랐습니다.
//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());
}
this
의 this.getDate()
의미는 어수선하지 않습니다
if (!(otherObject instanceof DateAndPattern)) {
.. 헤르 난과 스티브 쿠오 (단, 개인 취향의 문제임)에 동의하지만 그럼에도 불구하고 +1.
수퍼 클래스에는 java.lang.Object라는 두 가지 메소드가 있습니다. 우리는 그것들을 커스텀 객체로 재정의해야합니다.
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
즐기세요! @. @
멤버 평등을 확인하기 전에 클래스 평등을 확인하는 몇 가지 방법이 있으며 올바른 환경에서 두 가지 모두 유용하다고 생각합니다.
instanceof
연산자를 사용하십시오 .this.getClass().equals(that.getClass())
.final
등호 구현 에서 # 1을 사용 하거나 등호 알고리즘을 규정하는 인터페이스를 구현할 때 ( java.util
컬렉션 인터페이스 등 (obj instanceof Set)
구현중인 인터페이스 를 확인하는 올바른 방법 ). 대칭 특성을 손상시키기 때문에 equals를 무시할 수있는 경우 일반적으로 나쁜 선택입니다.
옵션 # 2를 사용하면 등호를 무시하거나 대칭을 위반하지 않고 클래스를 안전하게 확장 할 수 있습니다.
클래스가 또한 Comparable
인 경우 equals
and compareTo
메소드도 일관성이 있어야합니다. Comparable
클래스 의 equals 메소드에 대한 템플릿은 다음과 같습니다 .
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;
}
}
final
, 그리고 compareTo()
방법은 정렬 순서를 반대로 오버라이드 (override)하고, 서브 클래스와 슈퍼 클래스의 인스턴스는 동일한 것으로 간주되어서는 안된다. 이러한 객체를 트리에서 함께 사용하면 instanceof
구현 에 따라 "동일한"키를 찾지 못할 수 있습니다.
동등한 경우 Angelika Langer 의 Equals of Equals 를 살펴보십시오 . 나는 그것을 아주 좋아합니다. 또한 Generics in Java 에 대한 훌륭한 FAQ이기도합니다 . 그녀의 다른 기사 ( 여기서는 "Core Java"로 스크롤)에서 Part-2 및 "혼합 유형 비교"를 진행합니다. 재미있게 읽어보세요!
equals () 메소드는 두 객체의 동등성을 결정하는 데 사용됩니다.
int 값 10은 항상 10과 같습니다. 그러나이 equals () 메소드는 두 객체의 동등성에 관한 것입니다. 우리가 객체를 말할 때 속성이 있습니다. 평등을 결정하기 위해 이러한 속성이 고려됩니다. 동등성을 결정하고 클래스 정의 및 컨텍스트와 관련하여 결정할 수있는 모든 특성을 고려해야 할 필요는 없습니다. 그런 다음 equals () 메서드를 재정의 할 수 있습니다.
equals () 메서드를 재정의 할 때마다 항상 hashCode () 메서드를 재정의해야합니다. 그렇지 않은 경우 어떻게됩니까? 응용 프로그램에서 해시 테이블을 사용하면 예상대로 작동하지 않습니다. hashCode는 저장된 값의 동등성을 결정하는 데 사용되므로 키에 대한 해당 값을 반환하지 않습니다.
주어진 기본 구현은 Object 클래스의 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
내가 찾은 문제 중 하나는 두 객체가 서로에 대한 참조를 포함하는 곳입니다 (예 : 모든 자식을 얻는 부모의 편리한 메소드와 부모 / 자식 관계).
이러한 종류의 것들은 예를 들어 Hibernate 매핑을 할 때 상당히 일반적입니다.
hashCode 또는 equals 테스트에 관계의 양쪽 끝을 포함하면 재귀 루프로 들어가서 StackOverflowException으로 끝날 수 있습니다.
가장 간단한 해결책은 메소드에 getChildren 콜렉션을 포함시키지 않는 것입니다.
equals()
. 미친 과학자가 나에게 사본을 만들면 우리는 동등 할 것입니다. 그러나 우리는 같은 아버지가 없을 것입니다.