[자바] equals, hashCode는 낯설어서

skyepodium·2021년 12월 25일
2
post-thumbnail
post-custom-banner

객체 비교에 사용되는 equals, hashCode 에 대해 알아봅시다.

혹시 그런적 있으신가요?

자바에서 문자열을 비교할때 ==을 사용한 경험을?

자바에서 문자열은 String 클래스의 객체이고 비교 방법은 다음과 같습니다.

  • == - 주소값 비교
  • equals - 값 비교

응? 지금까지 == 비교를 해도 괜찮았는데? 라고 하신다면 그것은 자바의 String pool 때문입니다.

아래의 코드에서 "모카"라는 문자열이 선언될 때 스트링 풀에 들어가고

이후에 또 모카라는 문자열을 생성하려고 할 때 스트링 풀에서 검색하고 동일한 주소값을 지니게 됩니다.

class Main {
    public static void main(String[] args) {

        String name1 = "모카";
        String name2 = new String("모카");
        // name1이 생성될 때 String pool에 모카라는 문자열이 들어갔고
        // name3가 생성될때 주소값을 참조합니다.
        String name3 = "모카";

        // 1. 주소값 비교
        System.out.println(name1 == name2);
        // 2. 문자열 값 비교
        System.out.println(name1.equals(name2));
        // 3. 주소값 비교
        System.out.println(name1 == name3);

        // 4. 주소값 출력
        System.out.println(System.identityHashCode(name1));
        System.out.println(System.identityHashCode(name2));
        System.out.println(System.identityHashCode(name3));
    }
}

그래서 정리하면, 자바에서 객체 비교는 ==이 아니고, equals를 사용합니다.

1. equals

1) 예시

강아지는 1) 이름, 2) 나이를 가지는데, 이 두개의 정보가 같으면 똑같은 객체라고 가정하겠습니다.

자바에서 어떻게 비교해야할까요? 위에서 주소비교는 ==, 값 비교는 equals라고 했으니 equals를 사용하면 될까요?

ㅠ아닙니다. false가 나옵니다.

class Main {
    public static void main(String[] args) {

        Dog dog1 = new Dog("모카", 1);
        Dog dog2 = new Dog("모카", 1);

        // equals 비교 - false
        System.out.println(dog1.equals(dog2));
    }
}

class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

2) overide

자바의 모든 클래스는 Object 클래스를 상속받습니다. 그런데 Object 클래스를 눌러보면 equals가 다음과 같이 정의되어 있습니다.

ㄷㄷ 겉은 equals로 값 비교처럼 해놓고, 내부는 주소값 비교입니다.

public boolean equals(Object obj) {
    return (this == obj);
}

사실, String 클래스는 equals를 재정의해서 값 비교로 사용하고 있습니다.

그래서 우리도 재정의 합니다.

3) equals 재정의

age와 name의 값 비교를 진행해줍니다.

age는 원시타입이기 때문에 ==로 동등비교를 사용했습니다. name은 문자열로 내부적으로 == 비교와 equals 비교를 or 조건으로 사용합니다.

인텔리제이의 generate 기능을 사용했습니다.

import java.util.Objects;

class Main {
    public static void main(String[] args) {

        Dog dog1 = new Dog("모카", 1);
        Dog dog2 = new Dog("모카", 1);

        // equals 비교 - false
        System.out.println(dog1.equals(dog2));
    }
}

class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    // 인텔리제이 generate 기능 사용
    public boolean equals(Object o) {
        // 1. 주소값 비교
        if (this == o) return true;

        // 2. 인스턴스 여부 확인
        if (!(o instanceof Dog)) return false;

        // 3. 나이, 이름 비교
        Dog dog = (Dog) o;
        return age == dog.age && Objects.equals(name, dog.name);
    }
}

오 이제 true까지 잘 찍힙니다.

또 다른 예시

모카의 이름으로 HashMap 자료형에모카집이라고 등록했습니다. 그런데 값이 같은 다른 객체로 꺼내면 null로 아무것도 안나옵니다.

import java.util.HashMap;
import java.util.Objects;

class Main {
    public static void main(String[] args) {

        Dog dog1 = new Dog("모카", 1);
        Dog dog2 = new Dog("모카", 1);

        HashMap<Dog, String> dogHouse = new HashMap<>();
        dogHouse.put(dog1, "모카집 입니다.");

        // 모카집
        System.out.println(dogHouse.get(dog1));
        // null
        System.out.println(dogHouse.get(dog2));
    }
}

class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    // 인텔리제이 generate 기능 사용
    public boolean equals(Object o) {
        // 1. 주소값 비교
        if (this == o) return true;

        // 2. 인스턴스 여부 확인
        if (!(o instanceof Dog)) return false;

        // 3. 나이, 이름 비교
        Dog dog = (Dog) o;
        return age == dog.age && Objects.equals(name, dog.name);
    }
}

이러한 이유는 HashMap, HashSet, HashTable이 동일한 객체인지 검사하는 방법은 다음고 같습니다.

따라서 hashCode도 재정의 해줘야합니다.

2. hashCode

1) 정의

hashCode 메서드는 객체 멤버변수의 값을 이용해서 특정한 고유한 hash를 반환하는 메서드입니다.

2) override

해시코드를 만드는 방법은 가이드도 있고 다양한데, 심플하게 만들면 다음과 같습니다.

    @Override
    // Objects 클래스는 임의의 개수만큼 객체를 받아 해시코드를 계산해줍니다.
    public int hashCode() {
        return Objects.hash(name, age);
    }

3) 확인

import java.util.HashMap;
import java.util.Objects;

class Main {
    public static void main(String[] args) {

        Dog dog1 = new Dog("모카", 1);
        Dog dog2 = new Dog("모카", 1);

        HashMap<Dog, String> dogHouse = new HashMap<>();
        dogHouse.put(dog1, "모카집");

        // 모카집 입니다.
        System.out.println(dogHouse.get(dog1));
        // null
        System.out.println(dogHouse.get(dog2));
    }
}

class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    // 인텔리제이 generate 기능 사용
    public boolean equals(Object o) {
        // 1. 주소값 비교
        if (this == o) return true;

        // 2. 인스턴스 여부 확인
        if (!(o instanceof Dog)) return false;

        // 3. 나이, 이름 비교
        Dog dog = (Dog) o;
        return age == dog.age && Objects.equals(name, dog.name);
    }

    @Override
    // Objects 클래스는 임의의 개수만큼 객체를 받아 해시코드를 계산해줍니다.
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

동일한 값이 잘 나옵니다.

3. 정리

객체의 equals 메서드를 정의했으면, 반드시 hashCode 메서드도 재정의하라.

profile
callmeskye
post-custom-banner

0개의 댓글