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
만약person
lazy가로드 되어도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()
너무.
ThingWithOptionSetA
a와 같을 수있다.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