Java - HashMap, equals / hashCode 메서드 재정의

김지원·2022년 8월 5일
0

JAVA 총 정리

목록 보기
8/11

HashMap

HashMap<K,V>

: 키(K)와 값(V)와 쌍(Pair)로 이루어진 것들의 나열

  • Map 자료구조의 한 종류
  • 순서(인덱스)가 없는 자료구조이다. 전적으로 키에 의존적임.

  • key : String / value : Integer

HashMap 메서드

put(K, V)

: 키-값 쌍에 대해 K 갹체와 V 객체 쌍을 추가한다. 해당 키를 가지는 쌍이 이미 존재한다면 값을 치환한다.

  • 60으로 치환된걸 볼 수 있고 즉, key는 절대로 겹칠 수 없다.

get(K)

: HashMap이 가지고 있는 쌍 중에 키가 전달된 K객체인 쌍의 값을 반환한다.(해당 키를 가지는 쌍이 없다면 null을 반환한다.

  • 내가 지정한 대로 설정이 되는 것을 볼 수 있다.
  • get에 전달한한 key를 가지는 쌍의 값(value)을 돌려준다.
  • 없는 키를 전달하면 null을 반환해준다.
  • null이 뜨는 이유는 제네릭의 타입은 레퍼런스이기 때문에 null을 반환해준다. 기초타입이였다면 기초타입에는 null이 없기 때문에 불가능해진다.

clear()

: HashMap이 가지고 있는 키-값 쌍을 모두 제거한다.

size()

: HashMap이 가지고 있는 키-값 쌍의 개수를 반환한다.

remove(K)

: 키-값 쌍 중 키가 전달된 K 객체와 같은 쌍을 체거한다.

containsKey(K)

: 키-값 쌍 중 키가 전달된 K 객체와 같은 쌍이 있는가에 대한 여부를 반환한다.

  • value를 적으면 false가 뜬다.

containsValue(V)

: 키-값 쌍 중 값이 전달된 V 객체와 같은 쌍이 있는가에 대한 여부를 반환한다.

getOrDefault(K, V)

: 키-값 쌍 중 키가 전달된 K 객체와 같은 쌍이 있다면 해당 쌍의 V 객체를, 없다면 전달된 V 객체를 반환한다.

System.out.println(scores.get("최" == null ? 0 : scores.get("최")));
  • 이렇게 적는거랑 똑같다.
  • getOrDefault를 command 클릭해보면 똑같은 구조를 가지고 있다.

번호랑 이름을 멤버변수로 가지는 클래스 : Student 클래스 생성

→ main

  • 번호랑 이름이 같다면 현실 세상에서는 같은 사람이다. (같은 학년, 같은 반 전제)
  • 예를 들어 쉽게 풀어보자면 1학년 1반 1번 이라는 OMR카드가 2개가 있다. 이 두개의 OMR은 다른 "객체"지만 같은 사람이 썼다고 생각할 수 있다.
    즉, Student의 객체는 다르지만 같은 사람이라고 볼 수 있다.

프로그래밍 언어로 확인을 해보면...

System.out.println(kim1 == kim2); // false

객체를 비교하기 때문에 false가 뜬다.

System.out.println(kim1.equals(kim2));  // false

equlas 타고 들어가보면 결국 객체를 비교하고 있기 때문에 false가 뜬다. true를 뜨게 하려면 어떻게 할까?
😺 프로그램이 kim1과 kim2가 같은 사람으로 인식될 수 있도록 해보자.

  • equals 메서드는 object 타입을 받기 때문에 () 안에 아무거나 들어와도 오류는 발생하지 않지만 이것 또한 false가 뜬다. 재정의를 하기 위해서는 타입이 달라지면 안된다. 우리는 kim1이랑 kkk이랑 같을 수 없는 걸 안다.
    student 객체가 다른 student 객체와 같은지를 확인하기 위해서는 두 개의 타입이 같아야한다. 타입이 다른 것들을 equals를 재정의해서 다 여기서 쳐내야한다.

equals 메서드 재정의

  • equals 메서드 재정의를 하자.

@Override
public boolean equals(Object obj) {
    if(!(obj instanceof Student)) {
        return false;
    }
	Student student = (Student) obj;
	return this.number == student.number;
}
!(obj instanceof Student)
  • obj 객체가 Student 타입으로 강제 형변환 될 수 없으면?
    return false를 해준다.
  • if 문에 들어왔다는 것은 애시당초에 들어온 객체가 Student타입이 아니였다는 것임으로 false를 리턴해주면 된다.

kim1.equals("kkk"); 이렇게 호출하게 되면 아래와 같은 흐름이 된다.

obj : "kkk"
→ "kkk" instanceof Student
→ return false

→ "kkk"는 Student 타입으로 형변환 될 수 없음으로 false가 뜬다.

if문에 들어오지 않고 밖으로 바로 갔다는 것은 그자리에서 메서드 종료가 되었다는 것이고 강제 형변환해도 된다는 말이다.

Student student = (Student) obj;
return this.number == student.number;

같은학생인지 확인을 하기 위해서는 들어온 obj를 Student 타입으로 형변환해주고 난 후 그 둘의 number가 같은지 확인하면 된다.

return this.number == student.number;

여기서 this가 가르키는 것은 메서드를 호출하고 있는 대상 자체가 this이며 즉, kim1을 의미한다.

System.out.println(kim1.equals(kim2)); 

kim2가 ( )안에 들어갈 수 있는 이유는 부모타입은 자식객체를 받을 수 있기 때문에 가능하게 되는 것이다.

강제 형변환이 가능하기 때문에 kim2는 if문 자체에 들어가지 않는다.
if문 아래로 가서 다시 강제형변환 절차를 거치게 되고 this = kim1 이기에 kim1의 번호와 kim2의 번호가 같은지에 대한 여부를 반환하게 된다.
❗️ 따라서 번호가 같으면 같은 학생이라고 나온다.


그렇다면 eqauls 메서드를 왜 사용했고 HashMap과 어떤 관련이 있는 걸까?

equals( ) and hashCode( )

if(this==o) 

같은 객체라면 return true해주고

o == null || getClass() != o.getClass() 
(!(o instanceof Student)) 같은 의미이다.

getClass()를 하면 클래스의 타입이 나온다.
전달받은 object가 null이거나 this의 getClass( )가 전달받은 object의 getClass( ) 와 다르다면 return false를 해준다. 즉 instanceof를 한다는 의미이다.

Student student = (Student) o;
return number == student.number;

강제형변환하여 같은 것인지 검사한다.

자바 내부적으로 hashCode로 비교를 하는 경우가 많기 때문에 사실 두 객체의 같고 다름을 비교하기 위해서는 equals 뿐만 아니라 hashCode로도 비교를 해줘야한다.

  • 이 두개의 hashCode를 찍어보면 똑같은 값이 나오고 equals해보면 true가 출력 된다.
    hashCode가 의미하는 것은 주소값 비슷한 것이다. 객체가 가진 고유의 값이라고 생각하자.
    그 고유의 값을 String이 재정의해서 문자열의 내용을 기준으로 생성이 된다.
    그래서 내용이 같을 때 eqauls 했을 때 true가 뜨는 것이다.

hashCode 메서드 재정의

return Objects.hash(number);

Objects 클래스가 가진 hash메서드를 불러온다.

  • 괄호안에 비교기준이 되는 멤버변수를 정의해주면 된다. = number
  • Objects라는 클래스를 통해서 접근했기 때문에 hash메서드는 정적이다.

  • hashCode를 재정의해주면 같은 값이 나온다.

  • 비록 kim1이랑 kim2가 같은 내용을 가지고 있고 equals 메서드를 재정의 했음에도 불구하고 hashCode를 재정의하지 않게 되면 주소값 기준으로 계산되어서 나온다.
    다른 변수이기 때문에 stack에 있는 각각이 가진 주소값은 다름으로 hashCode또한 다르게 나온다.

  • hashCode를 재정의를 하면 값을 kim1로 넣었고 kim2로 나왔지만 100점이 나오게 된다.
    같은 학생인가에 대한 여부를 번호로 지정을 했기 때문에 같은 학생으로 인식이 되어서 100점이 나오게 된다.

  • hashCode를 재정의하지 않으면 null이 뜬다.

equals( ) and hashCode( ) 재정의

ArrayList에도 똑같이 작동한다.

  • 같은가에 대한 비교할 대상을 설정한다.
  • hashCode를 가지고 올 항목을 선택한다. (int타입 number)
 String msg1 = new String("Hello");
 String msg2 = new String("Hello");
 ArrayList<String> strings = new ArrayList<>();
 strings.add(msg1);
 System.out.println(strings.contains(msg2)); // true

분명히 msg1만 add 헀는데 msg2를 가지고 있냐고 물어봤을 때 true라고 해준다. 이렇게 작동될 수 있는 이유는 string의 hashCode가 재정의되어있기 때문이다. 같은 문자열이면 같은 hashCode가 나올 수 있도록 한다.

ArrayList는 equals를 사용하고 HashMap은 hashCode를 사용함으로 쓰는 게 다 달라서 둘 다 처리를 해주는 것이다.

HashCode는 객체가 가진 주소값과 유사하게 객체의 고유한 숫자 값이지만 간혹 객체간의 비교를 위해(equals와 더불어) 재정의 하기도 한다.

profile
Software Developer : -)

0개의 댓글