[Java] equals와 hashCod 재정의

고동이의 IT·2021년 11월 2일
1

Java

목록 보기
13/37
post-thumbnail

넘나 어려웠던 부분,, 복습을 통해 완벽하게 이해해보자!

equals()

2개의 객체가 가리키는 곳이 동일한 메모리 주소일 경우에만 동일한 객체가 되고 주소값이 다른 객체는 서로 다른 객체로 판단하는 메서드. ==와 같은 기능을 가짐

equals 메서드를 재정의하는 이유를 이해하기 위해선 equals메서드의 역할에 대해 제대로 이해해야한다.

우리가 equals 메서드를 주로 사용할때는 String타입의 값을 비교할때이다. 예를들어

 String str1 = "배";
 String str2 = "배";
 boolean temp = str1.equals(str2) ;
 // true

라는 변수를 정의하고 이 둘이 일치하는지 equals 메서드를 통해 확인할 수 있다.

이처럼 동일한 문자열을 2개 생성하면 2개의 문자열은 서로 다른 메모리 상에 할당된다. 그럼 이 문자열의 주소는 서로 다르기때문에 equals메서드의 반환값은 false여야하는거아니야?!!싶지만

equals메서드를 사용하면 true인 이유는 String 클래스에서 알아서 자동으로 친절하게도 equals 메소드를 오버라이드하여 문자를 비교하는 코드를 추가했기 때문이다.

아 그럼 우리가 직접 equals 메서드를 재정의해줄때는 언젠대?

예시를 통해 이해해보자

[Person 클래스]
person 클래스에는 id와 name 이 있다.

class Person{ 
//아무것도 상속받지 않으면 자동으로 object 객체상속 그래서 equals 쓸수있음

private int id;
private String name;


public int getId() {
	return id;
}
public void setId(int id) {
	this.id = id;
}
public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}

[Main메서드]
2개의 person는 동일한 id와 Name을 갖게된다.

public class Memo {
public static void main(String[] args) {
	
	 
	Person p1 = new Person(); //
	p1.setId(1);
	p1.setName("홍길동");
	
	Person p2 = new Person();
	p2.setId(1);
	p2.setName("홍길동");
	
	
	System.out.println(p1==p2); //false 주소값
	System.out.println(p1.equals(p2)); //true 값내용자체
	
}
}

위의 과정을 거쳐 2개의 person는 동일한 id와 Name을 갖게된다. 때문에 이 둘을 ==나 equals로 비교하면 true값이 반환되어야하는데 실행시켜보면 둘다 false가 반환된다,,,!!

왜이럴까?

다시 차근차근 생각해보자

앞에서 equals 메서드는 주소값이 다른 객체는 서로 다른 객체로 판단한다. 즉 2개의 객체가 가리키는 곳이 동일한 메모리 주소일 경우에만 동일한 객체가 된다.

그렇지만 그런거 알빠아니고요 우리가 equals로 비교할때는 객체가 같은지가 아니라 고유의 값이 같은지가 알고 싶은것이다!!

결론은 인스턴스가 둘 이상 만들어졌을때 equals 재정의 없이 우리가 쓰는 용도로 인스턴스를 비교하면 안됨.
재정의 안하면 그 클래스의 인스턴스는 오직 자기 자신과만 같게되기 때문이다.

두 객체를 같게하려면 클래스의 Person 클래스에 equals를 오버라이드하여 재정의해야함

equals 재정의 - 객체의 id값과 name값이 같으면 true를 반환하도록 재정의한다.

@Override
public boolean equals(Object obj) {
	if(obj==null) return false;
		
	// 같은 유형의 클래스인지 검사
	if(this.getClass() != obj.getClass()) return false;
		
	// 참조값이 같은지 검사. 참조값이 같으면 true
	if(this==obj) return true;
		
	// 매개변수에 저장된 객체를 현재 객체 유형으로 형변환 한다.
	Person that = (Person)obj;
		
	if(this.name==null && that.name!= null)
		return false;
		
	if(this.id==that.id && this.name==that.name)
		return true;
	if(this.id==that.id && this.name.equals(that.name))
		return true;
		
		return false;
	}
    

이제 equals에 의한 문제는 해결완,,,..
하지만 우리가 Person인스턴스를 HashSet과 같은 자료구조에 저장하려고 하면 또 다른 문제가 생기게 된다^_^.

계속해서 알아보자,,

hashCode()

두 객체의 동일성을 검사하는 연산자

  • HashSet, Hashtable, HashMap과 같이 Hash로 시작하는 컬렉션 객체들은 객체의 의미상의 동일성을 비교하기 위해서
    hashCode()메서드를 호출하여 비교한다. 그러므로, 객체가 같은지 여부를 결정하려면 hashCode()메서드를 재정의 해야한다.

이것이 equls를 재정의한 클래스에는 hashCode도 반드시 재정의해야하는 이유다. 뭔 소린지 크게 와닿지 않는다고?

천천히 알아보자 돈스탑 킵고잉

HashSet, Hashtable, HashMap과 같이 Hash로 시작하는 컬렉션 객체들은 자료를 저장하기 위한 위치를 선택하기 위해 hashCode를 이용한다.

예제를 보면

public static void main(String[] args) {


	Person p1 = new Person(); //
	p1.setId(1);
	p1.setName("홍길동");
	
	Person p2 = new Person();
	p2.setId(1);
	p2.setName("홍길동");

	Person p3 = p1;
	
	System.out.println(p1==p2); //false 주소값
	System.out.println(p1.equals(p2)); 
        // equals 재정의 후 true!
	
	HashSet<Person> testSet = new HashSet<>();
	
	testSet.add(p1);
	testSet.add(p2);
	
	System.out.println("set의 크기 : "+ testSet.size());
	
	System.out.println("p1 : "+p1.hashCode());
	System.out.println("p2 : "+p2.hashCode());
	System.out.println("p3 : "+p3.hashCode());
	
	/*
	 - equals() 메서드 : 두 객체의 내용이 같은지 검사하는 연산자
	 - hashCode() 메서드 : 두 객체의 동일성을 검사하는 연산자
	 - HashSet, Hashtable, HashMap과 같이 Hash로 시작하는 컬렉션 객체들은 객체의 의미상의 동일성을 비교하기 위해서 
	   hashCode()메서드를 호출하여 비교한다. 그러므로, 객체가 같은지 여부를 결정하려면 hashCode()메서드를 재정의 해야한다.
	   
	   참고) hashCode() 메서드에서 사용하는 '해싱 알고리즘'은 서로 다른 객체들에 대해서 같은 hashcode가 발생할 수 있다.
	 
	 */

}

}
Object 클래스의 hashCode() 메소드는 해당 메모리 주소값을 반환한다.
그렇기 때문에 위의 p1과 p2는 다른 해시값을 반환할 것이고,
HashSet에는 2개의 객체가 서로 다른 위치에 저장될 것이다ㅠ

우리는 이러한 문제를 해결하기 위해 hashCode 메소드도
Person 클래스에 오버라이드하여 수정해주어야 한다.

[hashCod재정의 해주기 전]

	System.out.println("p1 : "+p1.hashCode());
	System.out.println("p2 : "+p2.hashCode());
	System.out.println("p3 : "+p3.hashCode());

의 결과값은 p1과 p3의 해시값은 동일하고 이 둘과 p2는 다른 해시 값을 가짐

[hashCod재정의]

@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + id;
	result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

p1, p2, p3의 해시값 이 서로 동일하게됨

번외) equals(Object)가 두 객체를 같다고 판단했으면, 두 객체의 hashCode 값은 항상 같다.

하지만 equals(Object)가 두 객체를 다르다고 판단했더라도, 두 객채의 hashCode 값은 같을 수 있다. (해시 충돌)

hashcode를 재정의 해주었으므로 같은 값을 가지는 객체는 같은 해시값을 갖는다.

profile
삐약..뺙뺙

0개의 댓글