자바의 모든 클래스는 Object 클래스를 상속받는다.
Object 클래스에는 equals()와 hashCode()라는 메서드가 선언되어 있어, 모든 클래스는 equals()와 hashCode()를 상속받는다.
equals()
equals() 메서드는 어떤 두 객체가 동일한지 비교할 때 사용한다.
이는 동일성(Identity)을 비교하는 것이다.
즉, 두 객체가 동일한 메모리 주소일 경우에만 동일한 객체가 된다.
하지만, 프로그래밍 상에서 같은 값을 가지는 객체를 같은 객체로 인식해야 하는데, 이러한 동등성(Equality)을 위해 equals() 메서드를 오버라이딩 해야 한다.
String name1 = new String("꽁탁");
String name2 = new String("꽁탁");
System.out.println(name1 == name2); // 주소 비교 : false
System.out.println(name1.equals(name2)); // 값 비교 true
String에서는 equals() 메서드가 오버라이드되어 동등성을 비교하도록 처리되어 있기 때문에 true 값을 반환하고 있다.
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
Person person1 = new Person("꽁탁");
Person person2 = new Person("꽁탁");
System.out.println(person1 == person2); // 주소 비교 : false
System.out.println(person1.equals(person2)); // 오버라이드하지 않아 주소 비교 : false
반면, Person 클래스는 equals() 메서드를 오버라이드 하지 않아 동일성 비교를 수행해 false를 반환한다.
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
}
Person person1 = new Person("꽁탁");
Person person2 = new Person("꽁탁");
System.out.println(person1 == person2); // 주소 비교 : false
System.out.println(person1.equals(person2)); // 동등 비교 : true
Person 클래스에서 name이 같다면 같은 객체로 인식하도록 equals() 메서드를 오버라이드 했다.
hashCode()
hashCode() 메서드는 실행 중에 객체의 유일한 integer 값을 반환한다.
객체의 메모리 주소를 이용해서 해시코드를 만들어 반환하기 때문에 객체마다 다른 값을 가지고 있다.
동일한 객체는 동일한 메모리 주소를 갖는다는 것을 의미하므로, 동일한 객체는 동일한 해시코드를 가져야 한다.
그렇기 때문에 equals() 메서드를 오버라이드 한다면, hashCode() 메서드도 함께 오버라이드 해야 한다.
또한, HashSet, HashMap, HashTable에서는 hashCode() 메서드로 반환된 해시코드 값이 같은지를 비교하며, 해시코드 값이 다르면 다른 객체로 판단한다.
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
}
Person person1 = new Person("꽁탁");
Person person2 = new Person("꽁탁");
System.out.println(person1.hashCode()); // hashCode : 1057941451
System.out.println(person2.hashCode()); // hashCode : 1975358023
HashSet<Person> people = new HashSet<>();
people.add(person1);
people.add(person2);
System.out.println(people.size()); // size : 2
Person 클래스에 hashCode() 메서드를 오버라이드하지 않았기 때문에, person1과 person2의 해시코드 값이 다르며 HashSet에 넣었을 때 2개가 들어있는 것을 볼 수 있다.
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
Person person1 = new Person("꽁탁");
Person person2 = new Person("꽁탁");
System.out.println(person1.hashCode()); // hashCode : 1444287
System.out.println(person2.hashCode()); // hashCode : 1444287
HashSet<Person> people = new HashSet<>();
people.add(person1);
people.add(person2);
System.out.println(people.size()); // size : 1
hashCode() 메서드를 오버라이드하자, person1과 person2의 해시코드가 동일한 것을 볼 수 있고, HashSet에서도 동일한 객체로 판단하여 1개만 들어있는 것을 볼 수 있다.
'CS > Java' 카테고리의 다른 글
로깅 시 System.out.println()을 사용하지 않는 이유 (0) | 2023.08.13 |
---|---|
new String()과 리터럴("") (1) | 2023.08.12 |
String, StringBuffer, StringBuilder (0) | 2023.08.12 |
Java 컴파일 과정 (0) | 2023.05.18 |
직렬화(Serialization) (1) | 2022.09.26 |