[JAVA] hashcode(), equals() 재정의와 메모리 주소

DANI·2023년 11월 30일
1

JAVA를 공부해보자

목록 보기
16/29
post-thumbnail

toString() 재정의를 하지 않았을 때 @해시코드라고 알고 있었는데 System.identityHashCode()를 사용했을 때 나오는 숫자와 달랐다.

차이가 무엇일까?


public class Main {
    public static void main(String[] args) {
    int[] str6 = {1,2,3,4,5,6};

        System.out.println("==============================");
        System.out.println(str6);
        System.out.println(System.identityHashCode(str6));
        System.out.println(str6.hashCode());
        System.out.println("==============================");
    }
}

🔵 실행결과

==============================
[I@776ec8df
2003749087
2003749087
==============================



해쉬코드 2003749087를 16진수로 변환한 값이 @776ec8df 이다.





그렇다면 hashcode()와 메모리 주소는 같은 것일까?

@IntrinsicCandidate
public native int hashCode();

개체의 해시 코드 값을 반환합니다. 
이 메서드는 java.util.HashMap에서 제공하는 것과 
같은 해시 테이블의 이점을 위해 지원됩니다.
hashCode의 일반 계약은 다음과 같습니다:
자바 응용 프로그램을 실행하는 동안 
동일한 객체에 대해 두 번 이상 호출될 때마다, 
객체에 대한 동등한 비교에 사용되는 정보가 수정되지 않는다면
hashCode 메서드는 동일한 정수를 일관성 있게 반환해야 합니다. 
이 정수는 응용 프로그램의 한 실행에서 동일한 응용 프로그램의 
다른 실행으로 일관성을 유지할 필요는 없습니다.
동일한 방법에 따라 두 개체가 동일한 경우 두 개체 각각에 대해 
hashCode 메서드를 호출하면 동일한 정수 결과가 생성되어야 합니다.

동일한 방법에 따라 두 개체가 동일하지 않은 경우 두 개체 각각에 대해 해시 코드 메서드를 호출하면 서로 다른 정수 결과가 생성될 필요는 없습니다. 그러나 프로그래머는 동일하지 않은 개체에 대해 서로 다른 정수 결과를 생성하면 해시 테이블의 성능이 향상될 수 있음을 알아야 합니다.
반환:
이 개체의 해시 코드 값입니다.
구현 요구사항:
class Object에 의해 정의된 hashCode 메서드는 합리적으로 실용적인 경우 고유한 개체에 대해 고유한 정수를 반환합니다.
참고 항목:
equals(오브젝트), System.identityHashCode

hashCode는 단순히 고유한 개체에 고유한 정수를 반환을 하지, 메모리주소로 무조건 구현되지는 않는다




🔍 hashcode(), equals() 재정의

💾 Animal 클래스

package ex03;

import lombok.*;

import java.util.Objects;

@AllArgsConstructor
@NoArgsConstructor
public class Animal {
    private String name;
    private String type;
    private String cry;

}

💾 AnimalMain

package ex03;

public class AnimalMain {
    public static void main(String[] args) {
        Animal animal = new Animal("야옹이", "포유류", "야옹");
        Animal animal1 = new Animal("야옹이", "포유류", "야옹");
        System.out.println(animal.hashCode());
        System.out.println(animal1.hashCode());
        System.out.println(animal.equals(animal1));
    }
}

🔵 실행결과

1324119927
990368553
false

두 객체는 다른 해쉬값을 갖고, 다른 객체라고 판단한다. 하지만 두 객체는 갖고 있는 데이터가 동일하다. 이 때, 같은 객체라고 판단하기 위해서는 hashcode()equals()의 재정의가 필요하다


💾 Animal 클래스 ( equals() 재정의 )

package ex03;

import lombok.*;

import java.util.Objects;

@AllArgsConstructor
@NoArgsConstructor
public class Animal {
    private String name;
    private String type;
    private String cry;.

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Animal animal = (Animal) o;
        return Objects.equals(name, animal.name) && Objects.equals(type, animal.type) && Objects.equals(cry, animal.cry);
    }
}

🔵 실행결과

1324119927
990368553
true

true 값이 나왔다. 하지만 해쉬값은 다르게 나온다. 이 때, 같은 데이터면 같은 해쉬값을 호출하도록 해쉬코드를 재정의 하자


💾 Animal 클래스 ( hashCode() 재정의 )

package ex03;

import lombok.*;

import java.util.Objects;

@AllArgsConstructor
@NoArgsConstructor
public class Animal {
    private String name;
    private String type;
    private String cry;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Animal animal = (Animal) o;
        return Objects.equals(name, animal.name) && Objects.equals(type, animal.type) && Objects.equals(cry, animal.cry);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, type, cry);
    }
}

🔵 실행결과

-1620938993
-1620938993
true

해쉬값도 동일하게 출력된다




❓ 그렇다면 왜 해쉬코드 까지 같이 재정의 해야하는 걸까? 같은 객체임을 확인하기만 하면 된다면 equals()만 재정의하면 되지 않을까?


다시, 해쉬코드를 재정의하지 않고 equal()만 재정의한 상태로 돌아가보자


💾 AnimalMain 수정

package ex03;

import java.util.HashSet;
import java.util.Set;

public class AnimalMain {
    public static void main(String[] args) {
        Animal animal = new Animal("야옹이", "포유류", "야옹");
        Animal animal1 = new Animal("야옹이", "포유류", "야옹");
        System.out.println(animal.hashCode());
        System.out.println(animal1.hashCode());
        System.out.println(animal.equals(animal1));

        Set<Animal> animals = new HashSet<>();
        animals.add(animal);
        animals.add(animal1);
        System.out.println(animals); // 2개가 나옴
        System.out.println(animals.size()); // 2
    }
}

🔵 실행결과

1324119927
990368553
true
[ex03.Animal@4eec7777, ex03.Animal@3b07d329]
2

두 객체는 같다고 판단했지만, 중복을 허용하지 않는 set으로 받았을 때 HashSethash값으로 객체를 판단하므로 두 객체가 다른 객체라고 판단, 둘 다 저장하게 된다.



해쉬코드 재정의를 하고 난 후 메인을 실행했을때

-1620938993
-1620938993
true
[ex03.Animal@9f626f0f]
1

같은 객체라고 판단하고, 1개만 추가된다.


  • Collection(HashMap, HashSet, HashTable)은 객체가 논리적으로 같은지 비교할 때 다음과 과정을 거친다.

  1. hashcode()값이 같은가? 같다면
  2. equals()값이 같은가? 같다면

    동등객체, 둘 중 하나라도 다르면 다른 객체라고 판단한다.




참고 : https://tecoble.techcourse.co.kr/post/2020-07-29-equals-and-hashCode/
https://hye0-log.tistory.com/48

0개의 댓글