참고: ==, equals() - 2. equals()
public class Student {
String name;
String number;
int birthYear;
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "사용자";
s1.number = "123";
s1.birthYear = 1995;
Student s2 = new Student();
s2.name = "사용자";
s2.number = "123";
s2.birthYear = 1995;
if (s1.equals(s2)) {
System.out.println("s1 == s2");
} else {
System.out.println("s1 != s2");
}
}
}
/* 실행결과
s1 != s2
*/
Student 객체의 s1과 s2는 입력된 값은 동일하나, 주소값이 다르기 때문
해시함수(임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수)를 통해 얻어지는 값
자료구조(HashTable 같은)를 사용할 때 데이터가 저장되는 위치를 결정하기 위해 사용
실행 중에(Runtime) 객체의 유일한 integer값을 반환
Object 클래스에서는 heap에 저장된 객체의 메모리 주소를 반환하도록 되어있다. (항상 그런 것은 X)
native 키워드
public native int hashCode();
public class Main {
public static void main(String[] args) {
Person p1 = new Person("Kim");
Person p2 = new Person(new String("kim"));
Person p3 = p2;
System.out.println(p1.hashCode()); // 실행결과 : 798154996
System.out.println(p2.hashCode()); // 실행결과 : 681842940
System.out.println(p3.hashCode()); // 실행결과 : 681842940
}
}
class Person {
String name;
Person(String name) {
this.name = name;
}
}
p2와 p3의 결과는 동일하다. 메모리에 할당된 주소값이 동일하기 때문이다.
Person 클래스를 작성한 이유?
Person 객체를 new 연산자를 통해 선언하여 사용하기 위해서는
Person이라는 클래스가 먼저 생성되어 있어야 사용가능하기 때문
public class Main {
public static void main(String[] args) {
// 실험 1
String str1 = "hello";
String str2 = "hello";
String str3 = "world";
System.out.println(str1.hashCode()); // 실행결과 : 99162322
System.out.println(str2.hashCode()); // 실행결과 : 99162322
System.out.println(str3.hashCode()); // 실행결과 : 113318802
// 실험 2
String str4 = new String("abc");
String str5 = new String("abc");
System.out.println(str4.hashCode()); // 실행결과 : 96354
System.out.println(str5.hashCode()); // 실행결과 : 96354
}
}
문자열이 동일한 str1과 str2의 hashCode 는 동일하다.
(str1과 str3는 문자열이 다르므로, 당연히 hashCode도 다르다)
str4와 str5는 서로 주소값이 다른 객체임에도 불구하고, hashCode가 동일하다.
String에서의 hashCode는 주소값이 아닌 문자열을 비교하기 때문이다.
동일한 객체는 동일한 메모리 주소를 갖는다는 것을 의미한다.
→ 따라서, 동일한 객체는 동일한 해시코드를 가져야 한다.
→ 즉, equals() 메소드를 오버라이드 한다면, hashCode() 메소드도 함께 오버라이드 되어야 한다.
Java 프로그램을 실행하는 동안
equals에 사용된 정보가 수정되지 않았다면, hashCode는 항상 동일한 정수값을 반환해야 한다.
두 객체가 equals()에 의해 동일하다면, 두 객체의 hashCode() 값도 일치해야 한다.
두 객체가 equals()에 의해 동일하지 않다면, 두 객체의 hashCode() 값은 일치하지 않아도 된다.
Employee 클래스는 id를 고유값으로 갖는 상황을 가정해보자.
public class Employee{
private Integer id;
private String firstname;
private String lastName;
private String department;
//Setters and Getters
}
같은 id 값을 갖는 2개의 Employ를 서로 다른 처리 과정에 의해 얻은 상황이지만, equals 연산 시 false 를 반환해버린다.
public class EqualsTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
System.out.println(e1.equals(e2)); //false
}
}
이를 해결하기 위해, 오버라이딩을 한다.
Employ에 다음과 같은 equals 메소드를 오버라이딩해준다.
@Override
public boolean equals(Object o) {
if(o == null) {
return false;
}
if (o == this) {
return true;
}
if (getClass() != o.getClass()) {
return false;
}
Employee e = (Employee) o;
return (this.getId() == e.getId());
}
문제점
Employee 클래스를 자료구조(HashSet 같은)에 저장하려고 하면
e1과 e2는 다른 해시값을 반환하고, HashSet에는 2개의 객체가 서로 다른 위치에 저장된다.
→ 이런 문제를 해겨하기 위해, hashCode() 가 필요하다.
hashCode 메소드를 오버라이딩해준다.
public class EqualsTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Prints 'true' now!
System.out.println(e1.equals(e2));
Set<Employee> employees = new HashSet<Employee>();
employees.add(e1);
employees.add(e2);
System.out.println(employees); //Prints two objects
}
}
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + getId();
return result;
}
그 결과, hashSet에도 1개의 데이터만 저장되게 된다.
SutdaCard클래스를 HashSet에 저장하고 출력하는 예제이다. HashSet에 중복된 카드가 저장되지 않도록 SutdaCard의 hashCode( )를 알맞게 오버라이딩하시오. (String클래스의 hashCode( )를 사용하라)
import java.util.*;
class SutdaCard {
int num;
boolean isKwang;
SutdaCard() {
this(1, true);
}
SutdaCard(int num, boolean isKwang) {
this.num = num;
this.isKwang = isKwang;
}
public boolean equals(Object obj) {
if (obj instanceof SutdaCard) {
SutdaCard c = (SutdaCard) obj;
return num == c.num && isKwang == c.isKwang;
} else {
return false;
}
}
public String toString() {
return num + (isKwang ? "K" : "");
}
}
class Exercise11_11 {
public static void main(String[] args) {
SutdaCard c1 = new SutdaCard(3, true);
SutdaCard c2 = new SutdaCard(3, true);
SutdaCard c3 = new SutdaCard(1, true);
HashSet set = new HashSet();
set.add(c1);
set.add(c2);
set.add(c3);
System.out.println(set);
}
}
/* 실행결과
[3K, 1K]
*/
// 정답
public int hashCode( ) {
return toString().hashCode();
}