최근에이 Developer Works Document를 읽었습니다 .
이 문서는 정의 hashCode()
하고 equals()
효과적이고 정확하게 작성 하는 데 관한 것이지만, 왜이 두 가지 방법을 재정의해야하는지 알 수 없습니다.
이러한 방법을 효율적으로 구현하기로 결정하려면 어떻게해야합니까?
최근에이 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
.
분명했으면 좋겠다
if you think you need to override one, then you need to override both of them
잘못되었습니다. hashCode
클래스가 재정의 equals
되지만 역이 아닌 경우 재정의해야 합니다.
equals
이 javadoc에 명시된 계약을 위반 한다는 점에 유의하십시오 Object
. "두 개의 오브젝트가 equals(Object)
메소드 에 따라 동일하면 두 오브젝트 hashCode
각각에 대해 메소드 를 호출 하면 동일한 정수 결과가 생성되어야합니다." 물론, 모든 계약의 모든 부분이 모든 코드에서 실행되는 것은 아니지만 공식적으로는 위반으로 간주되며, 버그가 발생하기를 기다리는 버그라고 생각합니다.
개체 의 해시 코드 값 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 장을 읽는 것 입니다.
hashCode()
.
간단히 말해 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");
동등성을 재정의하지 않으면 다음과 같은 결과를 얻습니다.
옳은? 아마도 이것이 당신이 원하는 것이라면 어쩌면. 그러나 두 개의 다른 인스턴스인지 여부에 관계없이 동일한 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()
.
자바는 규칙을
"Object 클래스 equals 메소드를 사용하여 두 오브젝트가 동일한 경우 해시 코드 메소드는이 두 오브젝트에 대해 동일한 값을 제공해야합니다."
따라서 클래스에서 equals()
재정의 hashcode()
하는 경우이 규칙을 따르도록 메소드 를 재정의해야합니다 . 예를 들어, 키와 값 쌍으로 값을 저장하기 위해 두 가지 방법 equals()
과 hashcode()
가 모두 사용됩니다 Hashtable
. 우리가 하나를 재정의하고 다른 것을 재정의하지 않으면 Hashtable
그러한 객체를 키로 사용하면 원하는대로 작동하지 않을 수 있습니다.
당신이 그것들을 무시하지 않으면 당신은 Object의 기본 함침을 사용할 것입니다.
인스턴스 동등성과 hascode 값은 일반적으로 객체를 구성하는 요소에 대한 지식이 필요하므로 일반적으로 클래스에서 유형적 의미를 갖도록 재정의해야합니다.
@Lombo의 답변에 추가
언제 equals ()를 재정의해야합니까?
Object의 equals ()의 기본 구현은
public boolean equals(Object obj) {
return (this == obj);
}
즉, 두 개의 객체가 동일한 메모리 주소를 가진 경우에만 동일한 것으로 간주되며 객체를 자체와 비교하는 경우에만 적용됩니다.
그러나 하나 이상의 속성 값이 동일한 경우 두 객체를 동일하게 고려할 수 있습니다 (@Lombo의 답변에 제공된 예 참조).
따라서 equals()
이러한 상황에서 재정의 하고 평등 조건을 스스로 지정할 수 있습니다.
equals ()를 성공적으로 구현했으며 제대로 작동합니다. 왜 hashCode ()를 재정의하도록 요청합니까?
사용자 정의 클래스에서 "해시"기반 컬렉션 을 사용하지 않는 한 괜찮습니다. 하지만 당신은 할 수 있습니다 미래의 어떤 시간을 사용 HashMap
하거나 HashSet
하지 당신이 할 경우 override
와 해시 코드를 () "제대로 구현" , 이러한 해시 기반의 수집은 아닙니다 작동한다.
재정의는 같음 (@Lombo의 답변에 추가)
myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?
우선 해시 맵의 hashCode가와 second
같은지 확인합니다 first
. 값이 동일한 경우에만 동일한 버킷에서 동등성을 확인합니다.
그러나 여기서는 hashCode 가이 두 객체에 대해 다릅니다 (기본 구현과 다른 메모리 주소를 가지기 때문에). 따라서 평등을 확인하는 것조차 중요하지 않습니다.
재정의 된 equals () 메소드 내에 중단 점이 있으면 서로 다른 hashCode가있는 경우 들어 가지 않습니다.
contains()
확인 hashCode()
하고 동일한 경우에만 equals()
메소드를 호출합니다 .
모든 버킷에서 HashMap이 동일한 지 검사 할 수없는 이유는 무엇입니까? 따라서 hashCode ()를 재정의 할 필요가 없습니다 !!
그런 다음 해시 기반 컬렉션의 요점이 없습니다. 다음을 고려하세요 :
Your hashCode() implementation : intObject%9.
다음은 버킷 형태로 저장된 키입니다.
Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...
예를 들어지도에 키 10이 포함되어 있는지 알고 싶습니다. 모든 버킷을 검색 하시겠습니까? 또는 하나의 버킷 만 검색 하시겠습니까?
hashCode를 기반으로 10이 있으면 버킷 1에 있어야 함을 식별하므로 버킷 1 만 검색됩니다 !!
class A {
int i;
// Hashing Algorithm
if even number return 0 else return 1
// Equals Algorithm,
if i = this.i return true else false
}
hashCode()
버킷을 결정하는 데 사용하는 해시 값을 계산하고 값을 버킷 equals()
에 이미 있는지 여부를 확인하는 방법을 사용 합니다. 그렇지 않으면 다른 값이 추가되고 현재 값으로 대체됩니다hashCode()
먼저 항목 (버킷)
equals()
을 찾고 항목에서 값을 찾는 데 사용합니다.둘 다 재정의되면
<지도 >
Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...
equals가 재정의되지 않은 경우
<지도 >
Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..
hashCode가 재정의되지 않은 경우
<지도 >
Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...
해시 코드 동일 계약
1) 일반적인 실수는 아래 예에 나와 있습니다.
public class Car {
private String color;
public Car(String color) {
this.color = color;
}
public boolean equals(Object obj) {
if(obj==null) return false;
if (!(obj instanceof Car))
return false;
if (obj == this)
return true;
return this.color.equals(((Car) obj).color);
}
public static void main(String[] args) {
Car a1 = new Car("green");
Car a2 = new Car("red");
//hashMap stores Car type and its quantity
HashMap<Car, Integer> m = new HashMap<Car, Integer>();
m.put(a1, 10);
m.put(a2, 20);
System.out.println(m.get(new Car("green")));
}
}
녹색 자동차를 찾을 수 없습니다
2. hashCode ()로 인한 문제
재정의되지 않은 방법으로 인해 문제가 발생합니다 hashCode()
. 와 사이의 계약은 다음 equals()
과 hashCode()
같습니다.
두 객체의 해시 코드가 동일하면 같을 수도 있고 같지 않을 수도 있습니다.
public int hashCode(){
return this.color.hashCode();
}
값 객체를 사용할 때 유용합니다 . 다음은 포틀랜드 패턴 리포지토리 에서 발췌 한 것입니다 .
값 객체의 예는 숫자, 날짜, 돈 및 문자열과 같은 것입니다. 일반적으로 그것들은 아주 널리 사용되는 작은 물체입니다. 그들의 정체성은 그들의 객체 정체성보다는 상태에 근거한다. 이런 방식으로 동일한 개념적 값 객체의 사본을 여러 개 가질 수 있습니다.
따라서 1998 년 1 월 16 일을 나타내는 개체의 복사본을 여러 개 가질 수 있습니다. 이러한 복사본은 서로 동일합니다. 이와 같은 작은 개체의 경우 날짜를 나타 내기 위해 단일 개체를 사용하지 않고 새 개체를 만들어 이동하는 것이 더 쉽습니다.
값 객체는 항상 Java에서 .equals ()를 재정의해야합니다 (또는 Smalltalk에서 =). .hashCode ()를 재정의해야합니다.
equals와 hashcode 메소드는 객체 클래스에 정의되어 있습니다. 기본적으로 equals 메소드가 true를 리턴하면 시스템은 더 나아가 해시 코드의 값을 점검합니다. 두 객체의 해시 코드도 동일하면 객체는 동일한 것으로 간주됩니다. 따라서 equals 메서드 만 재정의하는 경우 재정의 된 equals 메서드가 2 개의 개체가 같음을 나타내더라도 시스템 정의 해시 코드는 2 개의 개체가 같음을 나타내지 않을 수 있습니다. 따라서 해시 코드도 무시해야합니다.
true
되는 항목 equals
이 일치하는 것으로 간주되지는 않습니다. 다른 한편으로, 컬렉션이 동일한 해시 코드를 가질 수 없다는 것을 알게되면, 그것들이 동등하다는 것을 알지 못할 것입니다.
Java의 동일 및 해시 코드 메소드
그것들은 모든 클래스의 슈퍼 클래스 인 java.lang.Object 클래스의 메소드입니다 (사용자 정의 클래스 및 Java API에 정의 된 클래스).
이행:
공개 부울 같음 (오브젝트 obj)
공개 int hashCode ()
공개 부울 같음 (오브젝트 obj)
이 방법은 단순히 두 객체 참조 x와 y가 동일한 객체를 참조하는지 확인합니다. 즉, x == y인지 확인합니다.
이 반사적이다 : 임의의 참조 값 x에 대해, x.equals (X)가 true를 반환한다.
대칭입니다. 모든 참조 값 x 및 y에 대해, y.equals (x)가 true를 반환하는 경우에만 x.equals (y)가 true를 반환해야합니다.
전 이적입니다. 모든 참조 값 x, y 및 z에 대해 x.equals (y)가 true를 반환하고 y.equals (z)가 true를 반환하면 x.equals (z)는 true를 반환해야합니다.
일관된 결과 : 모든 참조 값 x 및 y에 대해 x.equals (y)를 여러 번 호출하면 객체의 동등 비교에 사용 된 정보가 수정되지 않은 경우 지속적으로 true를 반환하거나 false를 반환합니다.
null이 아닌 참조 값 x의 경우 x.equals (null)은 false를 반환해야합니다.
공개 int hashCode ()
이 메소드는이 메소드가 호출 된 오브젝트의 해시 코드 값을 리턴합니다. 이 메소드는 해시 코드 값을 정수로 리턴하며 Hashtable, HashMap, HashSet 등과 같은 해시 기반 콜렉션 클래스의 이점을 위해 지원됩니다.이 메소드는 equals 메소드를 대체하는 모든 클래스에서 대체되어야합니다.
hashCode의 일반적인 계약은 다음과 같습니다.
Java 응용 프로그램을 실행하는 동안 동일한 객체에서 두 번 이상 호출 될 때마다 hashCode 메소드는 객체의 동등 비교에 사용 된 정보가 수정되지 않는 한 동일한 정수를 일관되게 반환해야합니다.
이 정수는 응용 프로그램의 한 실행에서 동일한 응용 프로그램의 다른 실행까지 일관성을 유지하지 않아도됩니다.
equals (Object) 메소드에 따라 두 오브젝트가 동일한 경우 두 오브젝트 각각에서 hashCode 메소드를 호출하면 동일한 정수 결과가 생성되어야합니다.
equals (java.lang.Object) 메소드에 따라 두 오브젝트가 동일하지 않은 경우 두 오브젝트 각각에서 hashCode 메소드를 호출하면 고유 한 정수 결과가 생성되어야합니다. 그러나 프로그래머는 동일하지 않은 객체에 대해 고유 한 정수 결과를 생성하면 해시 테이블의 성능을 향상시킬 수 있음을 알고 있어야합니다.
동일한 객체는 동일한 해시 코드를 생성해야하지만 동일하지 않은 객체는 고유 한 해시 코드를 생성 할 필요는 없습니다.
자원:
아래 예제에서 Person 클래스에서 equals 또는 hashcode에 대한 재정의를 주석 처리하면이 코드는 Tom의 순서를 찾지 못합니다. 해시 코드의 기본 구현을 사용하면 해시 테이블 조회에 실패 할 수 있습니다.
내가 가지고있는 것은 사람별로 사람들의 순서를 끌어내는 단순화 된 코드입니다. 해시 테이블에서 사람이 키로 사용되고 있습니다.
public class Person {
String name;
int age;
String socialSecurityNumber;
public Person(String name, int age, String socialSecurityNumber) {
this.name = name;
this.age = age;
this.socialSecurityNumber = socialSecurityNumber;
}
@Override
public boolean equals(Object p) {
//Person is same if social security number is same
if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
return true;
} else {
return false;
}
}
@Override
public int hashCode() { //I am using a hashing function in String.java instead of writing my own.
return socialSecurityNumber.hashCode();
}
}
public class Order {
String[] items;
public void insertOrder(String[] items)
{
this.items=items;
}
}
import java.util.Hashtable;
public class Main {
public static void main(String[] args) {
Person p1=new Person("Tom",32,"548-56-4412");
Person p2=new Person("Jerry",60,"456-74-4125");
Person p3=new Person("Sherry",38,"418-55-1235");
Order order1=new Order();
order1.insertOrder(new String[]{"mouse","car charger"});
Order order2=new Order();
order2.insertOrder(new String[]{"Multi vitamin"});
Order order3=new Order();
order3.insertOrder(new String[]{"handbag", "iPod"});
Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
hashtable.put(p1,order1);
hashtable.put(p2,order2);
hashtable.put(p3,order3);
//The line below will fail if Person class does not override hashCode()
Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
for(String item:tomOrder.items)
{
System.out.println(item);
}
}
}
문자열 클래스 및 랩퍼 클래스는 Object 클래스 equals()
와 다른 구현 및 hashCode()
메소드를 갖습니다 . Object 클래스의 equals () 메소드는 내용이 아닌 객체의 참조를 비교합니다. Object 클래스의 hashCode () 메서드는 내용이 같은지 여부에 관계없이 모든 단일 객체에 대해 고유 한 해시 코드를 반환합니다.
맵 콜렉션을 사용할 때 문제가 발생하고 키가 영구 유형, StringBuffer / 빌더 유형입니다. String 클래스와 달리 equals () 및 hashCode ()를 재정의하지 않기 때문에 equals ()는 내용이 같더라도 두 개의 다른 객체를 비교할 때 false를 반환합니다. hashMap이 동일한 콘텐츠 키를 저장하게합니다. 동일한 콘텐츠 키를 저장한다는 것은 Map이 중복 키를 전혀 허용하지 않기 때문에 Map 규칙을 위반한다는 의미입니다. 따라서 클래스의 hashCode () 메소드뿐만 아니라 equals () 메소드를 대체하고 구현 (IDE는 이러한 메소드를 생성 할 수 있음)을 제공하여 String의 equals () 및 hashCode ()와 동일하게 작동하고 동일한 컨텐츠 키를 방지합니다.
equals ()가 해시 코드에 따라 작동하므로 equals ()와 함께 hashCode () 메서드를 재정의해야합니다.
또한 equals ()와 함께 hashCode () 메서드를 재정의하면 equals ()-hashCode () 계약을 그대로 유지하는 데 도움이됩니다. "두 객체가 같으면 해시 코드가 같아야합니다."
hashCode ()에 대한 사용자 정의 구현을 언제 작성해야합니까?
우리가 알고 있듯이 HashMap의 내부 작업은 해싱 원칙에 있습니다. 엔트리 세트가 저장되는 특정 버킷이 있습니다. 동일한 범주 객체를 동일한 인덱스에 저장할 수 있도록 요구 사항에 따라 hashCode () 구현을 사용자 정의합니다. put(k,v)
메소드를 사용하여 값을 맵 콜렉션에 저장하는 경우 put ()의 내부 구현은 다음과 같습니다.
put(k, v){
hash(k);
index=hash & (n-1);
}
즉, 인덱스를 생성하고 인덱스는 특정 키 객체의 해시 코드를 기반으로 생성됩니다. 따라서 동일한 해시 코드 항목 세트가 동일한 버킷 또는 인덱스에 저장되므로이 방법으로 요구 사항에 따라 해시 코드를 생성하십시오.
그게 다야!
hashCode()
메소드는 주어진 객체에 대해 고유 한 정수를 얻는 데 사용됩니다. 이 정수는이 개체가 일부에 저장 될 필요가있을 때, 버킷 위치를 결정하기 위해 사용되는 HashTable
, HashMap
데이터 구조 등을 포함한다. 기본적으로 Object의 hashCode()
메소드는 객체가 저장된 메모리 주소의 정수 표현을 반환합니다.
hashCode()
객체의 방법은 우리가에 삽입 할 때 사용되는 HashTable
, HashMap
또는 HashSet
. HashTables
Wikipedia.org에 대한 자세한 내용 은 참조하십시오.
지도 데이터 구조에 항목을 삽입하려면 키와 값이 모두 필요합니다. 키와 값이 모두 사용자 정의 데이터 유형 인 경우 키의 값은 hashCode()
오브젝트를 내부적으로 저장할 위치를 결정합니다. 지도에서 객체를 조회해야하는 경우 키의 해시 코드에 따라 객체를 검색 할 위치가 결정됩니다.
해시 코드는 내부적으로 특정 "영역"(또는 목록, 버킷 등) 만 가리 킵니다. 다른 키 객체는 잠재적으로 동일한 해시 코드를 가질 수 있기 때문에 해시 코드 자체가 올바른 키를 찾도록 보장하지는 않습니다. 그런 HashTable
다음이 영역 (해시 코드가 동일한 모든 키)을 반복하고 키의 equals()
방법을 사용 하여 올바른 키를 찾습니다. 올바른 키를 찾으면 해당 키에 저장된 객체가 반환됩니다.
보시다시피, hashCode()
와 equals()
메소드를 조합 하여 객체를 저장하거나 조회 할 때 사용됩니다 HashTable
.
노트:
항상 동일한 속성을 사용하여 생성 hashCode()
하고 equals()
둘 다 생성하십시오 . 우리의 경우와 마찬가지로 직원 ID를 사용했습니다.
equals()
일관성이 있어야합니다 (객체가 수정되지 않은 경우 계속 같은 값을 반환해야 함).
때마다 a.equals(b)
, 다음 a.hashCode()
과 같은 동일해야합니다 b.hashCode()
.
하나를 재정의하면 다른 것을 재정의해야합니다.
http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html
hashCode()
모든 객체에 대해 고유 한 정수를 반환하는 데 사용되는 것은 아닙니다. 불가능합니다. 네 번째 단락의 두 번째 문장에서이 내용을 모순했습니다.
IMHO, 그것은 규칙에 따라-두 객체가 동일하면 동일한 해시를 가져야합니다. 즉, 동일한 객체는 동일한 해시 값을 생성해야합니다.
위에서 주어진 Object의 기본 equals ()는 ==로, 주소를 비교하고, hashCode ()는 주소를 정수로 반환합니다 (실제 주소에서 hash).
해시 기반 컬렉션에서 사용자 정의 객체를 사용해야하는 경우 equals () 및 hashCode ()를 모두 재정의해야합니다. 두 개의 다른 직원 객체를 재정의 할 수 있습니다. 이는 연령을 hashCode ()로 사용할 때 발생하지만 직원 ID가 될 수있는 고유 한 값을 사용해야합니다.
해시 코드는 항상 숫자를 반환하기 때문에 알파벳 키 대신 숫자를 사용하여 객체를 검색하는 것이 항상 빠릅니다. 어떻게됩니까? 다른 객체에서 이미 사용 가능한 값을 전달하여 새 객체를 만들었다 고 가정합니다. 이제 전달 된 값이 같기 때문에 새 객체는 다른 객체와 동일한 해시 값을 반환합니다. 동일한 해시 값이 반환되면 JVM은 매번 동일한 메모리 주소로 이동하며 동일한 해시 값에 대해 둘 이상의 객체가있는 경우 equals () 메서드를 사용하여 올바른 객체를 식별합니다.
Map에서 사용자 정의 객체를 키로 저장하고 검색하려면 항상 사용자 정의 Object에서 equals 및 hashCode를 재정의해야합니다. 예 :
Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");
여기서 p1 & p2는 하나의 객체로 간주되며 map
크기는 동일하므로 크기는 1입니다.
public class Employee {
private int empId;
private String empName;
public Employee(int empId, String empName) {
super();
this.empId = empId;
this.empName = empName;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
@Override
public String toString() {
return "Employee [empId=" + empId + ", empName=" + empName + "]";
}
@Override
public int hashCode() {
return empId + empName.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(this instanceof Employee)) {
return false;
}
Employee emp = (Employee) obj;
return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
}
}
테스트 클래스
public class Test {
public static void main(String[] args) {
Employee emp1 = new Employee(101,"Manash");
Employee emp2 = new Employee(101,"Manash");
Employee emp3 = new Employee(103,"Ranjan");
System.out.println(emp1.hashCode());
System.out.println(emp2.hashCode());
System.out.println(emp1.equals(emp2));
System.out.println(emp1.equals(emp3));
}
}
객체 클래스에서 equals (Object obj)는 주소 비교를 비교하는 데 사용되므로 테스트 클래스에서 두 객체를 비교하는 경우 메소드는 false를주는 것과 같지만 hashcode ()를 재정의하면 내용을 비교하고 적절한 결과를 얻을 수 있습니다.
오버라이드 equals()
하지 않으면 hashcode()
, 당신 또는 다른 사람이 같은 클래스 타입을 해시 콜렉션에서 사용하지 않으면 문제가 발생하지 않을 것이다 HashSet
. 저의 사람들은 문서화 된 이론을 여러 번 명확하게 설명했습니다. 저는 아주 간단한 예를 제시하려고 여기 있습니다.
equals()
커스터마이징 된 것을 의미 해야하는 클래스를 생각해보십시오 .
public class Rishav {
private String rshv;
public Rishav(String rshv) {
this.rshv = rshv;
}
/**
* @return the rshv
*/
public String getRshv() {
return rshv;
}
/**
* @param rshv the rshv to set
*/
public void setRshv(String rshv) {
this.rshv = rshv;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Rishav) {
obj = (Rishav) obj;
if (this.rshv.equals(((Rishav) obj).getRshv())) {
return true;
} else {
return false;
}
} else {
return false;
}
}
@Override
public int hashCode() {
return rshv.hashCode();
}
}
이제이 주요 클래스를 고려하십시오 :-
import java.util.HashSet;
import java.util.Set;
public class TestRishav {
public static void main(String[] args) {
Rishav rA = new Rishav("rishav");
Rishav rB = new Rishav("rishav");
System.out.println(rA.equals(rB));
System.out.println("-----------------------------------");
Set<Rishav> hashed = new HashSet<>();
hashed.add(rA);
System.out.println(hashed.contains(rB));
System.out.println("-----------------------------------");
hashed.add(rB);
System.out.println(hashed.size());
}
}
다음과 같은 결과가 나옵니다.
true
-----------------------------------
true
-----------------------------------
1
결과에 만족합니다. 그러나 재정의 하지 않으면 동일한 멤버 콘텐츠 hashCode()
를 Rishav
가진 객체 가 더 이상 고유 한 것으로 취급되지 않기 때문에 악몽을 일으킬 수 있습니다. hashCode
기본 동작으로 생성되면 다음과 같이 출력됩니다.
true
-----------------------------------
false
-----------------------------------
2
Bah- "equals ()를 재정의하는 모든 클래스에서 hashCode ()를 재정의해야합니다."
[조슈아 블로흐의 효과적인 자바에서?]
이것이 잘못된 방법이 아닙니까? hashCode를 재정의하면 아마도 해시 키 클래스를 작성하고 있음을 암시하지만 동등을 재정의하는 것은 확실하지 않습니다. 해시 키로 사용되지 않는 클래스가 많이 있지만 다른 이유로 논리적 평등 테스트 방법을 원합니다. "같음"을 선택하면이 규칙을 과도하게 적용하여 hashCode 구현을 작성해야 할 수 있습니다. 달성 할 수있는 모든 것은 코드베이스에 테스트되지 않은 코드를 추가하는 것입니다. 또한 필요하지 않은 코드를 작성하는 것은 민첩하지 않습니다. 그것은 단지 잘못입니다 (그리고 ide 생성 된 것은 아마도 당신의 수공예품과 호환되지 않을 것입니다).
분명히 그들은 키로 사용되도록 작성된 객체에 대한 인터페이스를 위임해야합니까? 어쨌든 Object는 기본 hashCode () 및 equals () imho를 제공하지 않아야합니다. 아마도 많은 깨진 해시 컬렉션을 장려했을 것입니다.
어쨌든, 나는 "규칙"이 앞뒤로 쓰여졌다 고 생각합니다. 그 동안 평등 테스트 방법에 "같음"을 사용하지 않는 것이 좋습니다 :-(