본문 바로가기
[IT] JAVA

[자바] equals와 hashcode - 같은 객체인지 비교

by 오리엔탈킴 2022. 2. 23.

안녕하세요, JAVA에서 두 변수가 같은지를 비교할 때, 주로 '==' 연산자와 equals()란 메서드를 사용합니다.

'==' 연산자와 equals() 메소드

만약 비교하는 대상의 데이터 타입이 기본 타입(Primitive Type : byte, short, int, long, char, float, double, boolean)이라면, '==' 연산자를 이용해서 해당 두 변수의 값이 같은지 비교해서 True / False를 리턴해줄 것입니다.

비교하는 데이터 타입이 참조 타입(Reference Type : Class, Interface, Array, Enum)이라면, '==' 연산자는 두 대상이 완전히 동일한 대상인지, 두 대상의 참조값 (메모리 주소)이 같은지를 비교하여 True / False를 리턴해줄 것입니다.

만약 참조 타입 두 대상의 내용이 같은지를 알고 싶다면, equals() 메서드를 통해 확인이 가능합니다.

public class CompareObject {

	public static void main(String[] args) {
		int int1 = 100;
		int int2 = 100;
		
		System.out.println("int 비교: " + (int1 == int2));
        // int 비교: true
		
		List<Integer> list1 = new ArrayList<>();
		List<Integer> list2 = new ArrayList<>();
		List<Integer> list3 = list1;
		
		list1.add(100);
		list2.add(100);

		System.out.println("List 비교#1: " + (list1 == list2));
		// List 비교#1: false
		System.out.println("List 비교#2: " + (list1 == list3));
		// List 비교#2: true
		System.out.println("List 비교#3: " + (list1.equals(list2)));
        // List 비교#3: true
	}

}

 

equals() 메서드 Overriding

우리가 생성한 객체(Object)의 내용이 같은지 비교도 equals() 메서드로 가능할지 한번 해보도록 하겠습니다. 아래와 같이 User라는 간단한 객체를 만들고, ID와 Name의 값이 같은 2개의 객체를 생성해서 비교해보도록 하겠습니다.

class User {
	
	private int ID;
	private String Name;
	
	public User(int iD, String name) {
		super();
		ID = iD;
		Name = name;
	}

}

public class CompareObject {

	public static void main(String[] args) {
		User user1 = new User(1,"kim");
		User user2 = new User(1,"kim");
		
		System.out.println("객체 비교#1: " + (user1 == user2));
        // 객체 비교#1: false
		System.out.println("객체 비교#2: " + (user1.equals(user2)));
        // 객체 비교#2: false
	}

}

 

'==' 연산자는 user1과 user2가 각각 생성된 객체이기 때문에 false가 나올 텐데, equals() 메서드의 결과 또한 false가 나왔습니다. 

자바 Object.class에 공통 메서드인 equals()를 보니 아래와 같이 결국 '==' 연산자를 통해 구현이 되어 있어서 결과가 위와 같이 나오는 것을 확인했습니다.

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

 

eqauls() 메서드가 두 user 객체의 내용을 비교하는 기능을 하도록 하려고 하면, User 객체 내에 eqauls 메서드를 Overriding하여 재정의를 해줘야합니다. 너무나 자주 생성되는 코드이기때문에, 거의 모든 IDE에서 eqauls메서드를 오버라이딩하는 코드를 자동으로 구현할 수 있도록 되어있습니다.

  • Eclipse : 객체에서 마우스 우클릭 > Source > Generate hashCode() and equals()
  • IntelliJ : 객체에서 마우스 우클릭 > Generate > equals() and hashCode()

이클립스에서 코드 자동생성
Eclipse
IntelliJ

 

 

아래와 같이 equals 메소드를 오버 라이딩하는 소스를 자동으로 생성한 뒤 돌려보면 equals() 결과가 true로 나오는 것을 확인할 수 있습니다.

String, ArrayList 등과 같은 자바 공통 클래스의 경우에는 이미 equals 메서드가 오버라이드 되어 있기 때문에 별도의 재정의 없이 사용이 가능합니다. 

class User {

    private int ID;
    private String Name;

    public User(int iD, String name) {
        super();
        ID = iD;
        Name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return ID == user.ID && Objects.equals(Name, user.Name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(ID, Name);
    }
}
public class CompareObject {

    public static void main(String[] args) {
        User user1 = new User(1,"kim");
        User user2 = new User(1,"kim");

        System.out.println("객체 비교#1: " + (user1 == user2));
        // 객체 비교#1: false
        System.out.println("객체 비교#2: " + (user1.equals(user2)));
        // 객체 비교#2: true
    }

}

 

hashCode()

위에서와 같이 IDE에서 equals를 재정의할 때 항상 hashCode라는 메서드도 같이 쌍으로 재정의 되는 것을 알 수 있는데요, 왜 항상 같이 움직이는 알아보겠습니다.

먼저, hashCode란 메모리 주소를 통해 만든 Hash 코드 값으로 Hash를 이용하는 Collection (HashMap, HashSet, HashTable)에서 두 객체가 같은 객체인지 비교를 위해, hashCode() 메서드의 리턴 값이 같은 지를 비교 후 equals() 메서드를 통해 비교를 합니다. 그래서 equals()만 재정의가 되어 있다면, HashMap이나 HashSet에서는 내용이 동일한 두 객체를 서로 다른 객체로 인식을 하게 됩니다. 해당 예제는 아래와 같습니다.

class User {

    private int ID;
    private String Name;

    public User(int iD, String name) {
        super();
        ID = iD;
        Name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return ID == user.ID && Objects.equals(Name, user.Name);
    }
    
/*    @Override
    public int hashCode() {
        return Objects.hash(ID, Name);
    }*/
}
public class CompareObject {

    public static void main(String[] args) {
        User user1 = new User(1,"kim");
        User user2 = new User(1,"kim");
        
        Map<User,String> map = new HashMap<>();
        
        map.put(user1, "최초등록");
        map.put(user2, "업데이트");

        System.out.println("결과: " + map.size());
        // 결과: 2
    }
}

 

Key값인 user1과 user2를 서로 같은 객체로 인식했다면, Update가 되어 Size가 1이 나왔을 텐데 hashCode() 재정의 한 부분을 주석 처리하였기 때문에 현재는 서로 다른 객체로 인식하여 Add가 된 상황이라 Size가 2가 나오게 됩니다. hashCode() 오버라이드 부분 주석을 제거하고 실행하면 Size가 1이 나오게 됩니다.

Lombok - @EqualsAndHashCode

만약 Lombok이 세팅이 되어 있다면 @EqualsAndHashCode 어노테이션을 추가하여 간단하게 구현이 가능합니다. 

아래 예제와 같이 @EqualsAndHashCode(exclude = "필드명")을 이용해서 ID만 같으면 같은 객체로 인식하겠다는 설정도 간단하게 구현이 가능합니다.

@AllArgsConstructor
@EqualsAndHashCode(exclude = "Name")
class User {

    private int ID;
    private String Name;

}
public class CompareObject {

    public static void main(String[] args) {
        User user1 = new User(1,"kim");
        User user2 = new User(1,"lee");
        
        Map<User,String> map = new HashMap<>();
        
        map.put(user1, "최초등록");
        map.put(user2, "업데이트");

        System.out.println("결과: " + map.size());
    }
}

 

 

반응형

댓글