equals()(javadoc)는 동등한 관계를 정의해야합니다 (반사적 인,대칭의, 및전 이적). 또한, 그것은 있어야합니다일관된(객체가 수정되지 않으면 동일한 값을 반환해야합니다). 더욱이,o.equals(null)항상 false를 돌려 줄 필요가 있습니다.
hashCode()(javadoc)도 있어야합니다.일관된(오브젝트가equals()같은 값을 반환해야합니다).
그만큼관계두 가지 방법 사이의 차이점은 다음과 같습니다.
할때는 언제나
a.equals(b), 그 다음에a.hashCode()~와 같아야합니다.b.hashCode().
하나를 재정의하는 경우 다른 하나를 재정의해야합니다.
계산에 사용하는 것과 동일한 필드 집합을 사용하십시오.equals()계산하기hashCode().
우수한 도우미 클래스 사용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, 또는약한지도 맵개체가 컬렉션에있는 동안 컬렉션에 넣은 키 개체의 hashCode ()가 변경되지 않도록하십시오. 이것을 보장하는 방탄 방법은 키를 변경하지 못하게하는 것입니다.다른 이점도 있습니다.
Hibernate와 같은 객체 - 관계 매퍼 (Object-Relationship Mapper, ORM)를 사용하여 지속되는 클래스를 다루는 경우,이 클래스가 부당하게 복잡하다고 생각하지 않는다면주의해야 할 몇 가지 문제가 있습니다.
게으른로드 된 객체는 하위 클래스입니다.
객체가 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만약personlazy가로드 되어도person.getName()로딩하고 "John Doe"를 반환합니다. 내 경험으로 볼 때,hashCode()과equals().
ORM을 다루는 경우 항상 getter를 사용하고 필드 참조는 절대로 사용하지 마십시오.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()(편집증 만) forequals(). 만약에 "충돌"의 위험이 있다면 괜찮습니다.hashCode(),하지만 결코 괜찮아요.equals().
hashCode()에서 속성의 변경되지 않는 하위 집합을 사용해야합니다.equals()
Saving an object will change it's state!hashCode돌아와야한다.int, 그래서 어떻게 사용 하시겠습니까?getName()? 당신에게 당신의 모범을 보여줄 수 있습니까?hashCode - jimmybondy
그 사실에 대한 설명obj.getClass() != getClass().
이 진술은equals()비우호적 인 유산. JLS (Java 언어 사양)에서는 ifA.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()
너무.
ThingWithOptionSetAa와 같을 수있다.Thing모든 추가 옵션에는 기본값이 있고,ThingWithOptionSetB, 다음 경우에 가능해야합니다.ThingWithOptionSetA비교할만한ThingWithOptionSetB두 객체의 모든 비 기본 속성이 기본값과 일치하는 경우에만 해당 객체를 테스트하는 방법을 볼 수 없습니다. - supercatB b2 = new B(1,99), 그 다음에b.equals(a) == true과a.equals(b2) == true그러나b.equals(b2) == false. - nickgrim
상속 친화적 인 구현을 위해서는 Tal Cohen의 솔루션을 확인하십시오.어떻게 equals () 메소드를 올바르게 구현합니까?
개요:
그의 책에서효과적인 자바 프로그래밍 언어 가이드(Addison-Wesley, 2001) 조슈아 블로흐 (Joshua Bloch)는 "인스턴스화 가능한 클래스를 확장하고 equals 계약을 유지하면서 aspect를 추가하는 방법은 없습니다." 탈 (Tal)은 의견이 맞지 않는다.
그의 해결책은 다른 비대칭적인 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);
}
}
equals ()는 상속 계층에서 작동해야합니다.리 스키프 대치 원리만족해야한다.
if (this.getClass() != o.getClass()) return false파생 된 클래스가 equals를 수정해야하는 경우에만 false를 반환한다는 점에서 유연합니다. 그게 맞습니까? - Aleksandr Dubinsky
아직도 그 누구도 구아바 도서관을 추천하지 않는다는 점에 놀랐다.
//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()아무것도 의미하지 않는다 (혼란을 제외하고) - Steve Kuoif (!(otherObject instanceof DateAndPattern)) {. hernan 및 Steve Kuo와 동의하지만 (개인 취향의 문제 임에도 불구하고) 그럼에도 불구하고 +1합니다. - Amos M. Carpenter
슈퍼 클래스에는 java.lang.Object라는 두 가지 메소드가있다. 그것들을 커스텀 객체에 오버라이드 (override)해야합니다.
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()).나는 #1을 사용한다.final구현과 같거나 equals 알고리즘을 규정하는 인터페이스를 구현할 때 (예 :java.util컬렉션 인터페이스 - 함께 확인하는 올바른 방법(obj instanceof Set)또는 당신이 구현하고있는 인터페이스). 대칭 속성을 깨뜨리기 때문에 equals를 재정의 할 수있는 경우 일반적으로 나쁜 선택입니다.
옵션 #2를 사용하면 equals를 재정의하거나 대칭을 깨지 않고도 클래스를 안전하게 확장 할 수 있습니다.
당신의 수업이 또한Comparable,equals과compareTo메소드도 일관성이 있어야합니다. 다음은 equals 메소드의 템플릿입니다.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;
}
}
final, 그리고compareTo()메서드가 오버라이드되어 정렬 순서가 바뀌 었으며 하위 클래스와 수퍼 클래스의 인스턴스를 동일하게 간주해서는 안됩니다. 이러한 객체들이 트리에서 함께 사용될 때, "동일하다" ~에 따르면instanceof구현을 찾지 못할 수도 있습니다. - erickson
equals () 메서드는 두 객체의 동등성을 결정하는 데 사용됩니다.
int 값 10은 항상 10과 같습니다. 그러나이 equals () 메서드는 두 객체의 동등성에 대한 것입니다. 우리가 객체라고 말할 때, 속성을 가질 것입니다. 동등성을 결정하기 위해 이러한 속성이 고려됩니다. 평등을 결정하기 위해 모든 속성을 고려해야 할 필요는 없으며 클래스 정의 및 컨텍스트와 관련하여 결정할 수 있습니다. 그런 다음 equals () 메서드를 재정의 할 수 있습니다.
equals () 메소드를 오버라이드 (override) 할 때마다 hashCode () 메소드를 항상 오버라이드 (override)해야합니다. 그렇지 않다면 어떻게됩니까? 애플리케이션에서 해시 테이블을 사용하면 예상대로 작동하지 않습니다. 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()
그러나아니그 반대도 마찬가지입니다!
내가 발견 한 한 가지 문제점은 두 객체가 서로에 대한 참조를 포함하는 부분입니다 (한 예는 모든 자식을 가져 오기 위해 부모에 대한 편리한 메소드와 부모 / 자식 관계입니다).
예를 들어, Hibernate 매핑을 할 때 이런 종류의 것들이 상당히 일반적입니다.
관계의 양쪽 끝을 hashCode 또는 equals 테스트에 포함 시키면 StackOverflowException으로 끝나는 재귀 루프로 들어갈 수 있습니다.
가장 간단한 해결책은 메서드에 getChildren 컬렉션을 포함하지 않는 것입니다.
equals(). 미친 과학자가 저를 복제 한 것이라면 우리는 동등 할 것입니다. 그러나 우리에게는 같은 아버지가 없을 것입니다. - Raedwald
instanceof첫 번째 피연산자가 null 인 경우 false를 반환합니다 (다시 Effective Java). - izaban