[Java] 동등성과 동일성

hyunoi·2024년 11월 7일
0

Java

목록 보기
4/20
post-thumbnail

동등성과 동일성


동등성(Identity)은 두 객체의 이 같음을 의미하고,
동일성(Equality)은 두 객체의 메모리 주소가 같음을 의미한다.

동등성의 경우 객체의 주소값이 다르더라도 값, 생긴 것이 똑같으면 두 변수는 동등하고,
동일성의 경우 객체의 값, 생긴 것이 똑같더라도, 객체의 주소값이 다르면 두 변수는 동일하지 않다.

자바에서 동등성은 equals(), 동일성은 ==으로 확인이 가능하다.

자바 코드로 확인해보자!

void test() {
    List<String> shape1 = List.of("square", "circle", "triangle");
    List<String> shape2 = List.of("square", "circle", "triangle");
    List<String> shape3 = shape1;

    System.out.println(shape1 == shape3); 		// true
    System.out.println(shape1 == shape2); 		// false
    System.out.println(shape1.equals(shape2)); 	// true
    System.out.println(shape1.equals(shape3)); 	// true
}
  1. shape1 == shape3
    shape3는 shape1을 직접 참조하고 있기 때문에 shape1과 shape3는 같은 객체를 가리킵니다.
    -> True; 동일성 O

  2. shape1 == shape2
    shape1과 shape2는 각각 List.of로 생성한 리스트이므로, 같은 값을 가지더라도 서로 다른 객체입니다. 그래서 == 비교에서는 false가 출력됩니다.
    -> False; 동일성 X

  3. shape1.equals(shape2)
    shape1과 shape2의 내용이 동일하기 때문에 equals 메서드는 두 리스트의 요소들을 비교하여 같음을 판단합니다. 따라서 true가 출력됩니다.
    -> True; 동등성 O

  4. shape1.equals(shape3)
    shape3는 shape1을 참조하고 있으므로 내용이 완전히 같습니다. equals 메서드가 두 리스트의 요소들을 비교하여 같음을 판단하므로 true가 출력됩니다.
    -> True; 동등성 O

String에서는?

String name1 = "hyunoi";
String name2 = "hyunoi";

System.out.println(name1 == name2);

위와 같이 코드를 작성하면 출력은?
True가 나온다!
==가 True이니, 동일성이 성립한다는 뜻이다.

String의 경우 String pool에서 관리하기 때문에 new를 이용해서 새로운 객체를 생성해주지 않으면 동일한 주소값을 가지게 된다.
name1과 name2가 같은 메모리 주소를 바라보게 되는 것

String name1 = "hyunoi";
String name2 = new String("hyunoi");

System.out.println(name1 == name2);

이렇게 하면 출력이 False가 나온다.
new로 새로운 객체를 만들어 주었기 때문에!

재정의를 해야 동등성 가능, equals

동등성 검사를 위해서 equals()를 사용하는데,
사실 재정의를 해준 후의 equals()가 우리가 아는 동등성 검사를 위해 쓰인다.

원래의 equals()==와 동일한 의미로 사용된다.
내용을 비교하고, 주소 값까지 비교하여 동일성을 판단하는 것이다.

그래서 동등성 검사를 위해서는 equals()의 오버라이딩이 필수이다.

class Person {
    private String name;
    private int age;

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

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);

        System.out.println(person1 == person2); 	 // false

        System.out.println(person1.equals(person2)); // false
    }
}

위 코드는 오버라이딩하지 않은 경우로,
person1과 person2를 다른 객체로 보아 동등성과 동일성 둘 다 False를 출력한다.

class Person {
    private String name;
    private int age;

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

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true; // 같은 객체일 경우 true
        if (obj == null || getClass() != obj.getClass()) return false;

        Person person = (Person) obj;
        // name과 age가 모두 같으면 true
        return name.equals(person.name) && age == person.age;
    }
}

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);

        System.out.println(person1 == person2);      // false

        System.out.println(person1.equals(person2)); // true
    }
}

이 코드는 오버라이딩을 한 코드이다.
equals()==과 다르게 동등성의 기능을 하는 것을 볼 수 있다!

hashCode()?

hashCode()는 객체의 해시 코드를 반환하는 메소드이다.
해시 코드는 객체를 구분하기 위해 사용되는 값으로, 객체의 내용이 바뀌지 않았다면 일관된 값을 반환해야 한다.

위에서 equals()를 오버라이딩한 것과 같이 hashCode()도 오버라이딩해서 동등한 객체의 경우 동일한 해시 코드를 가지게 해야 한다.
이렇게 해야 HashMap, HashSet, HashTable과 같은 컬렉션에서 객체를 올바르게 구분할 수 있다!

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);

        Set<Person> people = new HashSet<>();
        people.add(person1);
        people.add(person2);

        System.out.println(people.size()); // 2
    }
}

hashCode()를 오버라이딩하지 않은 코드이다.
HashSet은 person1과 person2를 각각의 해시 코드를 가진 객체로 판단하여 사이즈는 2가 된다.

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

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);

        Set<Person> people = new HashSet<>();
        people.add(person1);
        people.add(person2);

        System.out.println(people.size()); // 1
    }
}

hashCode()를 오버라이딩한 코드이다.
name과 age가 같으면 같은 객체로 판단하여 동일한 해시 코드를 반환하도록 하였다.
그래서 HashSet은 person1과 person2를 같은 해시 코드를 가진 하나의 객체로 판단하여 사이즈는 1이 된다.

주의💥

equals() 오버라이딩을 할 땐 혹시 모르니 hashCode()까지 오버라이딩하자~

0개의 댓글