[Java] equals와 == 연산자, 그리고 hashCode

✨New Wisdom✨·2020년 12월 5일
2

📕 Java 📕

목록 보기
4/24
post-thumbnail

Object 클래스

자바의 최상위 클래스이다.

인스턴스의 비교 equals 메소드

== 연산자는 참조변수의 참조 값을 비교한다.
따라서 서로 다른 두 인스턴스의 내용을 비교하려면 별도의 방법을 사용해야한다.
Object 클래스의 equals 메소드는 == 연산자와 마찬가지로 참조변수의 참조 값을 비교하도록 정의되어있다.
하지만 == 연산을 통해서도 참조 값 비교는 가능하기 때문에 equals 메소드는 내용 비교가 이뤄지도록 ✨오버라이딩✨ 하라고 있는 존재이다.

대표적인 예는 String 클래스이다.

import java.util.Scanner;

class StringEquality extends Exception {
    public static void main(String[] args) {
        String str1 = new String("So Simple");
        String str2 = new String("So Simple");
        
        /* 참조 대상을 비교하는 if~else 문 */
        if(str1 == str2) {
            System.out.println("str1, str2 참조 대상 동일하다.");
        } else {
            System.out.println("str1, str2 참조 대상 다르다.");
        }
        
        /* 두 인스턴스 내용을 비교하는 if~else 문 */
        if(str1.equals(str2)) {
            System.out.println("str1, str2 내용 동일하다.");
        } else {
            System.out.println("str1, str2 내용 다르다.");
        }
    }
}
str1, str2 참조 대상 다르다.
str1, str2 내용 동일하다.

참조변수의 참조 값을 비교하려면 == 연산,
인스턴스의 내용을 비교하려면 equals 를 사용하자!


2020.12.17 추가

코테를 준비하면서 우테코 전 기수 스켈레톤 코드에 hashCode() 가 존재하는 것을 보았다.
찾아보니 equls() 와 많이 비교하길래 추가로 정리해보겠다.

hashCode()

해시 코드란 객체를 식별할 수 있는 하나의 정수값을 뜻하는데, 주소와는 다른 개념으로 객체에 고유한 번호를 부여한다.

equals() vs hashCode()

  • equals : 두 객체의 내용이 같은지
  • hashCode : 두 객체가 같은 객체인지

인스턴스가 다르면 Object 클래스의 hashCode 메소드는 다른 값을 반환한다.
인스턴스가 다르면 Object 클래스의 equlas 메소드는 false를 반환한다.

따라서 값을 기준으로 동등 여부를 따지려면 두 메소드를 오버라이딩 해야한다.

class Car {
    private String model;
    private String color;
    
    public Car(String m, String c) {
        model = m;
        color = c;
    }

    @Override
    public String toString() {
        return model + " : " + color;
    }

    @Override
    public int hashCode() {
        return Object.hash(model, color);
    }
    
    @Override
    public boolean equals(Object obj) {
        String m = ((Car)obj).model;
        String c = ((Car)obj).color;

        if(model.equals(m) && color.equals(c))
            return true;
        else
            return false;
    }    
}

class HowHashCode{
    public static void main(String[] args) {    
        HashSet<Car> set = new HashSet<>();
        set.add(new Car("HY_MD_301", "RED"));
        set.add(new Car("HY_MD_301", "BLACK"));
        set.add(new Car("HY_MD_302", "RED"));
        set.add(new Car("HY_MD_302", "WHITE"));
        set.add(new Car("HY_MD_301", "BLACK"));

        System.out.println("인스턴스 수: " + set.size());

        for(Car car : set)
            System.out.println(car.toString() + '\t');
    }
}

위에서 말했듯이 equals()는 객체 내부의 내용이 동등한지를 비교하는데
이것을 논리적으로 동등하다고 표현한다.

이제 왜 Card 클래스에 오버라이드 된 hashCode와 equals 가 있는지 알았다.


HashSet을 이용해 Custom Class의 중복 제거

때문에 HashSet을 이용하여 Custom Class의 중복을 제거하는 것도 가능하다.
HashSet은 내부적으로 먼저 해당 객체의 hashCode()와 equals()를 실행해보기
때문에 해당 메소드들의 실행 결과로 두 객체가 같지 않으면 중복으로 제거하지 않는다.

따라서 Custom Class의 중복을 제거하는 HashSet을 이용하고 싶다면
해당 클래스에 equalshashCode를 Override해라!

Exmaple

Car
Wrapper class로 equalshashCode를 Override 하지 않은 상태

public class Car implements Comparable<Car> {
    private static final int MOVE_PIVOT = 4;

    private Name name;
    private int position;

    public Car(final Name name) {
        this(name, 0);
    }

    public Car(final Name name, final int position) {
        this.name = name;
        this.position = position;
    }

    public String getName() {
        return name.getName();
    }

    public int getPosition() {
        return this.position;
    }

	...
}

Cars
이 때 Cars에서 자동차들의 Name이 중복되는지 확인하는 메소드

    private static void validateNonDuplicatedNames(final List<Car> cars) {
        if (new HashSet<>(cars).size() != cars.size()) {
            throw new IllegalArgumentException("중복된 자동차 이름입니다.");
        }
    }
   

CarsTest
자동차 이름의 중복을 테스트한다.

public class CarsTest {
    @Test
    @DisplayName("자동차들 이름이 중복되었을 경우")
    void carsDuplicatedName() {
        assertThatThrownBy(() -> {
            Cars cars = Cars.makeFromCarNames(Arrays.asList(
                    new Name("pobi"),
                    new Name("pobi"),
                    new Name("pobi")));
        }).isInstanceOf(IllegalArgumentException.class);
    }

결과는 실패~!

Car
equalshashCode를 Override한다.

public class Car implements Comparable<Car> {
    private static final int MOVE_PIVOT = 4;

    private Name name;
    private int position;

    public Car(final Name name) {
        this(name, 0);
    }

    public Car(final Name name, final int position) {
        this.name = name;
        this.position = position;
    }

    public String getName() {
        return name.getName();
    }

    public int getPosition() {
        return this.position;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return position == car.position &&
                Objects.equals(name, car.name);
    }

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

Test 결과 성공 ~!

profile
🚛 블로그 이사합니다 https://newwisdom.tistory.com/

0개의 댓글