최근에이 Developer Works Document를 읽었습니다
.
이 문서는 정의 hashCode()
하고 equals()
효과적이고 정확하게 작성 하는 데 관한 것이지만, 왜이 두 가지 방법을 재정의해야하는지 알 수 없습니다.
이러한 방법을 효율적으로 구현하기로 결정하려면 어떻게해야합니까?
답변
Joshua Bloch는 효과적인 Java에 대해 말합니다.
equals ()를 재정의하는 모든 클래스에서 hashCode ()를 재정의해야합니다. 그렇게하지 않으면 Object.hashCode ()에 대한 일반 계약을 위반하게되어 클래스가 HashMap, HashSet 및 Hashtable을 포함한 모든 해시 기반 컬렉션과 함께 제대로 작동하지 못하게됩니다.
재정의 equals()
하지 않고 재정의하고을 hashCode()
사용하려고 시도 하면 어떻게 될지에 대한 예를 통해 이해하려고합시다 Map
.
우리는이 같은 클래스를 가지고 두 객체가 있다고 MyClass
자신의 경우 동일 importantField
IS는 (와 동일 hashCode()
하고 equals()
일식에 의해 생성)
public class MyClass {
private final String importantField;
private final String anotherField;
public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}
public String getEqualField() {
return importantField;
}
public String getAnotherField() {
return anotherField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}
무시 만 equals
equals
재정의 된 경우에만 myMap.put(first,someValue)
처음 호출 하면 일부 버킷으로 myMap.put(second,someOtherValue)
해시되고 다른 버킷으로 다른 버킷으로 해시됩니다 hashCode
. 따라서 동일한 버킷에 해시하지 않으므로 동일하지만 맵은이를 인식 할 수 없으며 둘 다 맵에 유지됩니다.
재정의 equals()
하면 재정의 할 필요는 없지만 hashCode()
, 두 MyClass
경우 importantField
가 동일하지만 재정의하지 않으면 이 두 개체 가 동일한 것을 알고있는이 특별한 경우에 어떤 일이 발생하는지 봅시다 equals()
.
무시 만 hashCode
당신이 이것을 상상해보십시오
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
재정의 hashCode
한 다음 호출 myMap.put(first,someValue)
할 때 먼저 걸리면 계산합니다.hashCode
주어진 버킷에 저장합니다. 그런 다음 전화 myMap.put(second,someOtherValue)
할 때 비즈니스 요구 사항에 따라 동일하므로 맵 문서에 따라 첫 번째로 두 번째로 바꿔야 합니다.
그러나 문제는지도 해시 그렇게 할 때, 등호 재정의되지 않은 것입니다 second
오브젝트가있는 경우 버킷 통해 반복보고 k
그러한second.equals(k)
이 같은 하나를 찾을 수 없습니다 사실이다 second.equals(first)
될 것입니다 false
.
분명했으면 좋겠다
답변
개체 의 해시 코드 값 HashMap
과 같은 컬렉션을 HashSet
사용하여 컬렉션 내에 개체를 저장하는 방법을 결정하고 컬렉션 에서 개체를 찾기 위해 해시 코드 를 다시 사용합니다.
해싱 검색은 2 단계 프로세스입니다.
- 올바른 버킷 찾기 (
hashCode()
) - 버킷을 사용하여 올바른 요소 검색 (
equals()
)
여기에 우리가 설정을 무시해야하는 이유에 작은 예입니다 equals()
및 hashcode()
.
Employee
나이와 이름이라는 두 가지 필드가 있는 수업을 생각해보십시오 .
public class Employee {
String name;
int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof Employee))
return false;
Employee employee = (Employee) obj;
return employee.getAge() == this.getAge()
&& employee.getName() == this.getName();
}
// commented
/* @Override
public int hashCode() {
int result=17;
result=31*result+age;
result=31*result+(name!=null ? name.hashCode():0);
return result;
}
*/
}
이제 클래스를 만들고에 Employee
객체를 삽입 HashSet
하고 해당 객체가 있는지 여부를 테스트하십시오.
public class ClientTest {
public static void main(String[] args) {
Employee employee = new Employee("rajeev", 24);
Employee employee1 = new Employee("rajeev", 25);
Employee employee2 = new Employee("rajeev", 24);
HashSet<Employee> employees = new HashSet<Employee>();
employees.add(employee);
System.out.println(employees.contains(employee2));
System.out.println("employee.hashCode(): " + employee.hashCode()
+ " employee2.hashCode():" + employee2.hashCode());
}
}
다음을 인쇄합니다.
false
employee.hashCode(): 321755204 employee2.hashCode():375890482
이제 uncomment hashcode()
method, 동일하게 실행하면 출력은 다음과 같습니다.
true
employee.hashCode(): -938387308 employee2.hashCode():-938387308
이제 두 객체가 같은 것으로 간주되면 해시 코드도 같아야하는 이유를 알 수 있습니까? 그렇지 않으면 두 개 이상의 객체가 동일한 것으로 간주되는 방식으로 메서드를 재정의 하더라도 Object 클래스 의 기본 해시 코드 메소드는 사실상 각 객체에 대해 항상 고유 번호를 갖기 때문에 객체를 찾을 수 없습니다.
equals()
. 해시 코드 가 그것을 반영하지 않으면 객체가 얼마나 동일한 지 중요 하지 않습니다. 따라서 한 번 더 : 두 객체가 같으면
해시 코드 도 같아야합니다.
답변
equals ()를 재정의하는 모든 클래스에서 hashCode ()를 재정의해야합니다. 그렇게하지 않으면 Object.hashCode ()에 대한 일반 계약을 위반하게되어 클래스가 HashMap, HashSet 및 Hashtable을 포함한 모든 해시 기반 컬렉션과 함께 제대로 작동하지 못하게됩니다.
에서 효과적인 자바 조슈아 블로흐,
정의 equals()
하고 hashCode()
일관되게 클래스의 유용성을 해시 기반 컬렉션의 키로 향상시킬 수 있습니다. hashCode에 대한 API 문서에서 설명하는 것처럼 “이 메소드는에서 제공하는 것과 같은 해시 테이블의 이점을 위해 지원됩니다 java.util.Hashtable
.”
이러한 메소드를 효율적으로 구현하는 방법에 대한 귀하의 질문에 대한 최선의 답변은 효과적인 Java의 3 장을 읽는 것 입니다.
답변
간단히 말해 Object의 equals-method는 참조가 동일한 지 확인합니다. 여기서 속성이 같을 때 클래스의 두 인스턴스가 의미 론적으로 동일 할 수 있습니다. 예를 들어 HashMap 및 Set 과 같이 equals 및 hashcode를 사용하는 컨테이너에 객체를 넣을 때 중요합니다 . 다음과 같은 클래스가 있다고 가정 해 봅시다.
public class Foo {
String id;
String whatevs;
Foo(String id, String whatevs) {
this.id = id;
this.whatevs = whatevs;
}
}
동일한 ID로 두 개의 인스턴스를 만듭니다 .
Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");
동등성을 재정의하지 않으면 다음과 같은 결과를 얻습니다.
- a.equals (b)는 서로 다른 두 인스턴스이므로 false입니다.
- a.equals (a)는 동일한 인스턴스이므로 true입니다.
- b.equals (b)는 동일한 인스턴스이므로 true입니다.
옳은? 아마도 이것이 당신이 원하는 것이라면 어쩌면. 그러나 두 개의 다른 인스턴스인지 여부에 관계없이 동일한 ID를 가진 객체가 동일한 객체가되기를 원한다고 가정 해 봅시다. 우리는 equals (및 해시 코드)를 재정의합니다.
public class Foo {
String id;
String whatevs;
Foo(String id, String whatevs) {
this.id = id;
this.whatevs = whatevs;
}
@Override
public boolean equals(Object other) {
if (other instanceof Foo) {
return ((Foo)other).id.equals(this.id);
}
}
@Override
public int hashCode() {
return this.id.hashCode();
}
}
equals와 hashcode 구현에 관해서는 Guava의 도우미 메소드를 사용하는 것이 좋습니다.
답변
정체성은 평등이 아닙니다.
- 운영자
==
테스트 ID 와 같습니다 . equals(Object obj)
방법은 평등 테스트를 비교합니다 (즉, 우리는 방법을 재정 의하여 평등을 알려야합니다)
Java에서 equals 및 hashCode 메소드를 대체해야하는 이유는 무엇입니까?
먼저 equals 메소드의 사용법을 이해해야합니다.
두 객체 간의 차이점을 식별하려면 equals 메소드를 재정의해야합니다.
예를 들면 다음과 같습니다.
Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.
------------------------------
Now I have overriden Customer class equals method as follows:
@Override
public boolean equals(Object obj) {
if (this == obj) // it checks references
return true;
if (obj == null) // checks null
return false;
if (getClass() != obj.getClass()) // both object are instances of same class or not
return false;
Customer other = (Customer) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference
return false;
return true;
}
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2); // returns true by our own logic
이제 hashCode 메소드는 쉽게 이해할 수 있습니다.
hashCode는 HashMap , HashSet 과 같은 데이터 구조에 객체를 저장하기 위해 정수를 생성합니다 .
Customer
위와 같은 재정의 equals 메소드를 가지고 있다고 가정합니다 .
customer1.equals(customer2); // returns true by our own logic
버킷에 객체를 저장할 때 데이터 구조로 작업하는 동안 (버킷은 폴더의 멋진 이름입니다). 내장 해시 기술을 사용하면 위의 두 고객에 대해 두 개의 다른 해시 코드가 생성됩니다. 그래서 우리는 동일한 동일한 객체를 서로 다른 두 곳에 저장하고 있습니다. 이러한 종류의 문제를 피하려면 다음 원칙에 따라 hashCode 메서드를 재정의해야합니다.
- 동일하지 않은 인스턴스는 동일한 해시 코드를 가질 수 있습니다.
- 동일한 인스턴스는 동일한 해시 코드를 반환해야합니다.
답변
좋아요, 개념을 아주 간단한 단어로 설명하겠습니다.
먼저 더 넓은 관점에서 우리는 컬렉션을 가지고 있으며 해시 맵은 컬렉션의 데이터 구조 중 하나입니다.
해시 맵이 무엇인지 그리고 무엇을 먼저 이해해야하는 경우 equals 및 hashcode 메소드를 모두 대체해야하는 이유를 이해하려면.
해시 맵은 키 값의 데이터 쌍을 배열 방식으로 저장하는 데이터 구조입니다. a []를 말하자. 여기서 ‘a’의 각 요소는 키 값 쌍입니다.
또한, 상기 어레이의 각 인덱스는 링크 된리스트 일 수 있으며, 이에 의해 하나의 인덱스에서 하나 이상의 값을 가질 수있다.
왜 해시 맵이 사용됩니까? 큰 배열 중에서 검색 해야하는 경우 효율적이지 않은 경우 각 배열을 검색해야하므로 해시 기술에 따르면 배열을 일부 논리로 사전 처리하고 해당 논리에 따라 요소를 그룹화 할 수 있습니다.
예 : 배열 1,2,3,4,5,6,7,8,9,10,11이 있고 해시 함수 mod 10을 적용하여 1,11이 함께 그룹화됩니다. 따라서 이전 배열에서 11을 검색해야하는 경우 전체 배열을 반복해야하지만 그룹화 할 때 반복 범위를 제한하여 속도를 향상시킵니다. 위의 모든 정보를 저장하는 데 사용되는 데이터 구조는 단순화를 위해 2D 배열로 생각할 수 있습니다.
이제 위의 해시 맵과 별도로 중복을 추가하지 않는다고 알려줍니다. 그리고 이것이 equals와 hashcode를 재정의 해야하는 주된 이유입니다.
따라서 hashmap의 내부 작업을 설명한다고 말하면 해시 맵이 어떤 메소드를 가지고 있고 위에서 설명한 위의 규칙을 어떻게 준수하는지 찾아야합니다.
따라서 해시 맵에는 put (K, V)이라는 메소드가 있으며 해시 맵에 따르면 배열을 효율적으로 분배하고 중복을 추가하지 않는 위의 규칙을 따라야합니다
따라서 넣은 것은 먼저 주어진 키에 대한 해시 코드를 생성하여 값을 가져갈 인덱스를 결정합니다. 해당 인덱스에 아무것도 없으면 새 값이 추가됩니다. 그런 다음 해당 색인에서 링크 된 목록의 끝에 새 값을 추가해야합니다. 그러나 원하는 해시 맵 동작에 따라 복제본을 추가해서는 안됩니다. 두 개의 Integer 객체 aa = 11, bb = 11이 있다고 가정하겠습니다. 객체 클래스에서 파생 된 모든 객체와 마찬가지로 두 객체를 비교하는 기본 구현은 객체 내부의 값이 아니라 참조를 비교하는 것입니다. 따라서 위의 경우 의미 론적으로 동일하더라도 동등성 테스트에 실패하고 동일한 해시 코드와 동일한 값을 갖는 두 개의 객체가 존재할 가능성이 있으므로 중복이 생성됩니다. 재정의하면 중복 추가를 피할 수 있습니다. 당신은 또한 참조 할 수 있습니다세부 작업
import java.util.HashMap;
public class Employee {
String name;
String mobile;
public Employee(String name,String mobile) {
this.name=name;
this.mobile=mobile;
}
@Override
public int hashCode() {
System.out.println("calling hascode method of Employee");
String str=this.name;
Integer sum=0;
for(int i=0;i<str.length();i++){
sum=sum+str.charAt(i);
}
return sum;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
System.out.println("calling equals method of Employee");
Employee emp=(Employee)obj;
if(this.mobile.equalsIgnoreCase(emp.mobile)){
System.out.println("returning true");
return true;
}else{
System.out.println("returning false");
return false;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee emp=new Employee("abc", "hhh");
Employee emp2=new Employee("abc", "hhh");
HashMap<Employee, Employee> h=new HashMap<>();
//for (int i=0;i<5;i++){
h.put(emp, emp);
h.put(emp2, emp2);
//}
System.out.println("----------------");
System.out.println("size of hashmap: "+h.size());
}
}
답변
hashCode()
:
해시 코드 방법 만 재정의하면 아무 일도 일어나지 않습니다. hashCode
각 객체에 대해 항상 새 클래스를 Object 클래스로 반환하기 때문 입니다.
equals()
:
동일한 방법 만 재정의하는 경우 a와 b는 동일하지만 발생하지 않아야 a.equals(b)
함을 의미합니다 hashCode
. hashCode
메소드를 대체하지 않았기 때문 입니다.
참고 : hashCode()
Object 클래스의 메소드는 hashCode
각 객체에 대해 항상 새로운 것을 반환 합니다.
당신이 해시를 기반으로 컬렉션 개체를 사용해야하는 경우에, 모두 오버라이드 (override) 할 필요가 그래서 equals()
및 hashCode()
.