ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 컬렉션에 저장된 객체 정렬하기 |컬렉션에 저장된 객체 비교하기 | Comparable vs Comparator | hashcode와 equals | 동일성과 동등성
    Java 2023. 5. 12. 15:02

    # 컬렉션에 저장된 객체를 정렬할 때

    컬렉션에는 참조타입, Wrapper class, 객체(Object)등을 저장할 수 있다.

    그리고 저장된 데이터를 정렬할 때는 Collections.sort() 메소드를 사용한다. 

     

    컬렉션에 저장된 타입이 참조타입이라면 sort 메소드를 바로 사용할 수 있다. 

    이미 참조타입의 클래스에서 비교 인터페이스를 구현하고 있기 때문이다.

     

     

    ex) String을 저장하고 있는 list 알파벳 순으로 정렬하기

    List<String> list = new ArrayList<>();
    list.add("banana");
    list.add("apple");
    list.add("orange");
    
    Collections.sort(list);

    하지만 저장하고 있는 타입이 객체라면, 반드시 비교 관련 인터페이스를 상속받아 지정된 메소드를 오버라이딩 해야한다. 

     

     

    # Comparable vs Comparator

    public interface Comparable<T> {
        public int compareTo(T o);
    }
    public interface Comparator<T> {
        int compare(T o1, T o2);
    }

    객체 비교 인터페이스엔 Comparable과 Comparator가 있다. 둘의 공통점은 인터페이스라 구현 시 compareTo과 compare라는 메소드를 반드시 오버라이딩 해야 한다는 것이다. 차이는 compareTo는 자기 자신(this)과 파라미터로 들어오는 객체를 비교하여 정렬하는 것이고, Comparator는 파라미터로 들어온 두 객체를 비교하여 정렬하는 방식이라는 점이다.

     

    보통 Comparable은 해당 클래스의 기본 정렬 순서를 정의하는데 사용되고, Comparator은 추가적인 정렬 기준을 제공하는데 사용한다. 

     

     

    ex) 컬렉션 내부에서 정렬 시 compareTo를 참고해서 정렬

    class Student implements Comparable<Student>{
    	int code;
    	String name;
    
    	public Student(int code, String name){
    		this.code = code;
    		this.name = name;
    	}
    
    	@Override
    	public int compareTo(Student other) {
        	// code 오름차순으로 정렬
    		return Integer.compare(this.code, other.code);
    	}
    
    }

    compareTo는 정수형인 int 타입을 반환한다. 자기 자신을 기준으로 비교할 객체의 차이를 비교하여 반환하는 것이다. 

    따라서 자기 자신과 같다면 0을, 자기 자신이 비교 객체보다 크면 양수, 자기 자신이 비교 객체보다 작으면 음수를 리턴하도록 한다.

     

     

    # 컬렉션에 저장된 객체를 비교해야하는 경우

    컬렉션에 객체를 저장하고 있다면 잊지말고 고려해줘야 할 사항이 있다.

    바로 객체의 동등성을 비교해야하는 경우엔 equals()와 hashcode()를 오버라이딩 해줘야한다는 점이다. 

     

    동등성은 두 객체가 다른 메모리 주소를 가지더라도 값이 같은지를 확인하기 위해 equals() 메소드를 사용하여 판단하는 개념이다.
    동일성은 두 객체가 같은 주소값을 가지는지 확인하기 위해 == 연산자를 사용하여 판단하는 개념이다.
    동일성은 동등성을 보장하지만 그 역은 성립하지 않는다.

     

    동등성을 비교해줘야하는 경우로는 두가지가 있다.

    1. 컬렉션 프레임워크의 객체를 검색하는 메서드를 사용하는 경우 

    2. 해시 기반 컬렉션을 사용하는 경우 

     

    컬렉션 프레임워크는 컬렉션에 저장된 데이터를 손쉽게 관리할 수 있도록 다양한 메소드를 제공하고 있다. 

    그 중 contains()는 해당 컬렉션에 저장된 데이터에서 같은 데이터가 있는지를 확인한 후 boolean 값을 리턴하는 메소드이다. 

     

    contains의 내부구조를 보면 컬렉션에 저장된 데이터를 순회하며 equals()를 사용해 같은 값을 가지고 있는지 확인하는 과정을 거친다.

    마찬가지로 String class도 equals를 오버라이딩 하여 문자열이 같은 값인지 비교하는 과정을 거친다.

     

    따라서 컬렉션안에 객체를 저장하고 있다면 equals 메소드를 오버라이딩해줘야 contains를 정확히 사용할 수 있는 것이다.  contains말고도 동일한 객체를 제거하는 remove(), 동일한 객체의 인덱스를 반환하는 IndexOf()등의 메소드도 마찬가지이다. 

     

     

    Student를 담는 컬렉션을 사용할때 contains를 사용할 일이 있기 때문에 아래와 같이 equals()를 구현해줬다. 

    class Student implements Comparable<Student>{
       // 생략
       
        @Override
    	public int hashCode() {
    		return Integer.hashCode(code);
    	}
        
    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj) // 동일성 비교. 같은 인스턴스이면 동등한 것이므로 바로 true리턴 
    			return true;
    		if (obj == null || getClass() != obj.getClass()) // 타입이 같은지 확인
    			return false;
    		Student other = (Student) obj;
    		return code == other.code; // 해당 객체의 동등성 조건은 코드가 같을 경우이므로 
    	}
        
    	@Override
    	public int compareTo(Student other) {
    		// 코드를 기준으로 오름차순 정렬
    		return Integer.compare(this.code, other.code);
    	}
    
    }

     

    여기서 한가지 중요한 사항이 있다. equals()를 재정의 할 때는 hashcode()도 반드시 재정의 해줘야 한다.

    equals()로 두 객체가 논리적으로 같은 객체임을 판단했다면 두 객체의 hashcode()는 똑같은 값을 반환해야하기 때문이다. hashcode()를 재정의하지 않았다면 같은 값을 가진 객체가 서로 다른 해시값을 가질 수 있다. 

     

    컬렉션 중 HashMap, HashSet, HashTable과 같은 해시기반의 컬렉션을 사용하는 경우라면 특히 그렇다.

    컬렉션 내부에서 객체를 저장하고 검색할 때 먼저 객체의 해시코드를 사용하여 동등성을 비교하기 때문이다. hashcode()로 두 객체의 해쉬코드값이 같다면 이후 equals()로 판단하는 로직을 거친다. 따라서 두 메서드를 구현하지 않으면 객체를 올바르게 저장하고 검색할 수가 없다. 

     

    객체에 hashcode()가 재정의 되어있지 않으면 Object클래스의 hashcode()메서드가 사용된다. 그러나 Obejct의 hashcode()는 객체마다 정확한 hashcode()값을 반환하지 않아 잘못된 결과가 도출 될 수 있다. 

    'Java' 카테고리의 다른 글

    인터페이스로 추상화하기 | 전략패턴  (0) 2023.05.17
Designed by Tistory.