관리 메뉴

개발노트

동일성(identity, ==)과 동등성(equality, equals()) 본문

Java

동일성(identity, ==)과 동등성(equality, equals())

YoonGwon 2021. 1. 18. 11:36

이번에는 자바의 동등성(equals) 동일성(==)에 대해 알아보자
일단 뭐 부터 해야될지 모르겠다.

간단하게 String 으로 먼저 알아보고 밑에선 hashcode 도 알아보자
우리는 String을 두가지 방법으로 초기화 할 수 있다.
다들 아시다시피

String temp = "hello";

String str = "hello";
String str1 = new String("hello");

위와 같은 방법으로 가능하다.
그럼 무엇이 다른가.

System.out.println(str == temp);
System.out.println(str1 == temp);

무엇이 나올까 고민해보자. true? false?
일단 저것을 알기전에 자바에선 기본자료형 참조자료형이 있다. 자세한건 구글링
말그대로 기본자료형 같은 경우에는 실제 값을 저장 하는 반면에 참조형은 실제 객체가 가르키는 주소이다.
첫번째 str은 기본 자료형으로 선언했고 str1은 참조형으로 선언 했다.
그럼 대충 감이 오지 않나.
첫번째는 true 이고 두번째는 false가 나온다.

그럼 int는 어떨까?

Integer temp = 123;

Integer i = 123;
Integer i1 = new Integer(123);

위와 같이 선언 했다고 가정해보자.

System.out.println(i == temp);
System.out.println(i1 == temp);

예상 했던거와 같이 true와 false가 나온다.
그런데 재미있는 사실은 다음과 같다.

 private static class IntegerCache {
        static final int low = -128;
        static {
            // high value may be configured by property
            int h = 127;
           ...
        }
    }

값을 128이상으로 바꾸어 보자. 혹은 -129이하로 그럼 원하던 값이 나오지 않는다.
Integer는 다음과 같이 캐싱을 한다.

private static class IntegerCache {
        static final int low = -128;
        static {
            // high value may be configured by property
            int h = 127;
           ...
        }
    }

아무튼 그렇다.
이번에 hashcode와 equals에 대해 알아보자.

class Product{

    private Long id;
    private String name;
    private Long price;

    public Product(Long id, String name, Long price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getPrice() {
        return price;
    }

    public void setPrice(Long price) {
        this.price = price;
    }
}

아주 평범한 자바빈즈 형태의 객체이다.

Product product1 = new Product(1L, "iphone", 99L);
Product product2 = new Product(1L, "iphone", 99L);

System.out.println(product1 == product2);
System.out.println(product1.equals(product2));

위와 같이 해보면 첫번째는 당연히 false가 나올테고 두번째는 기대 했던거와 달리 false가 나온다. (아닌가 ㅎㅎ)
어쨋든 우리는 true의 값을 기대 할려면 객체의 equals를 오버라이딩 해야된다.
오버라이딩 하지 않은 객체는 첫번째와 같이 레퍼런스를 비교한다.

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

위는 Object의 equals이다.

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Product product = (Product) o;
    return Objects.equals(id, product.id) &&
        Objects.equals(name, product.name) &&
        Objects.equals(price, product.price);
}

다음과 같이 오버라이딩을 하면 우리가 원하던 값을 얻을 수 있다.
equals은 IDE에서 생성해주는것으로 만들었다.

Set은 중복을 허용하지 않는다.
만약 Set에 product1, product2를 넣는 다면 어떻게 될까? 넣어보자.

Set<Product> products = new HashSet<>();
products.add(product1);
products.add(product2);
System.out.println(products.size());

중복 객체를 허용 하지 않는데 동등한 객체인대도 불구하고 size는 2가 나온다.
Set은 내부적으로 hashCode를 호출하여 비교를 한다.

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

우리는 위와 같이 hashCode를 오버라이딩 해야된다. 물론 HashSet은 HashMap으로 참조하여 구현 되었다.
아마두 컬렉션 대부분 hashCode를 호출하는 거 같다 (확인은 하지 않음)
다시 실행 해보면 우리가 원하는 결과인 size는 1이 나온다.
버그를 없애기 위해선 가능한 hashCode와 equals를 구현해주자

hashCode는 다음과 같은 규약이 있다.
만약 equals가 같다면 hashCode의 값도 같아야 하고
equals가 다르다면 hashCode는 다를수도 같을 수도 있지만 성능상 다른게 낫다.
마지막으로 hashCode가 같다고 해서 equals를 항상 true가 나오는 것은 아니다.
같은 해싱값이 나올 수 도 있다.

이렇게 자바의 동일성과 동등성에 대해 알아 봤다.

 

참조 : araikuma.tistory.com/329

728x90

'Java' 카테고리의 다른 글

[Java] 열거체 enum 클래스  (0) 2021.01.20
[JAVA] Collection 정리  (0) 2021.01.19