객체를 비교할 때 한 가지 기준만 있는 것은 아니다.

지난번, 글에서는 자바의 객체가 동일한지 아닌지에 대해서 equals와 hashCode라는 메서드를 오버 라이딩하는 것을 알아보게 되었습니다.

https://ksjm0720.tistory.com/28

 

 

[JavaBasic] 객체를 비교하는 방법들(1) - equals, hashCode

자바의 기본 구성 단위 객체 이 때까지 Python과 Javascript와 같은 다른 프로그래밍 언어를 다루어오다가 처음으로 Java를 다루게 되었습니다. 가장 차이가 나는 것은 바로 객체에 대한 상당한 의존

ksjm0720.tistory.com

하지만 이렇게 객체를 동일성을 비교하는 것뿐만이 아니라 서로의 순서를 비교해야할 때가 있습니다.

예시로 앞서 다루었던 코드를 다시 한번 보도록 하겠습니다.

 

public class People {
    private String lastName; // 성
    private String firstName; // 이름
    private int age; //나이
    
    public String getLastName() {
    	return this.lastName;
    }
    
    public String getFirstName() {
    	return this.firstName;
    }
    
    public int getAge() {
    	return this.age;
    }
}

 

여기서 필드는 lastName, firstName, 그리고 age가 존재를 합니다. 이때, 값을 기준으로 비교해주는 sort와 같은 메서드를 사용하게 되었을 때, 문제가 생기게 됩니다. 바로 객체의 어떤 필드를 기준을 하여 비교를 해줘야 하는지를 모른다는 것입니다.

 

"lastName, firstName 그리고 age 중 어떤 것을 기준으로 비교를해야하는 거지?"

 

이는 비단 이런 객체뿐만이 아니라, 원시값을 포장했을 때 필드가 하나여도, 자바에서는 어떤 값을 기준인지 판단하지 못합니다. 때문에 어떤 필드를 기준으로 하여 크기를 비교를 해줘야 할지 정해주어야 합니다.

 

예시 코드와 함께 문제를 해결해나가 보도록 하겠습니다.

 

import java.util.ArrayList;
import java.util.List;

public class Application {
    public static void main(String args[]) {
        People people1 = new People("Ga", "Ga", 10);
        People people2 = new People("Na", "Na", 20);
        People people3 = new People("Da", "Da", 30);

        List<People> peoples = new ArrayList<>();
        peoples.add(people1);
        peoples.add(people2);
        peoples.add(people3);

        peoples.sort();

        for (People people : peoples){
            System.out.println(people.getLastName() + " "+ people.getAge());
        }
    }
}

 

이때, 아직 비교할 기준을 설정해주지 않았기 때문에 people.sort()에서 문제가 생기게 됩니다.

 

사람의 나이를 내림차순으로 정렬하는 예제를 통해서 두 가지 방법에 대해서 알아보도록 하겠습니다.

 

객체에 비교할 수 있는 메서드를 내장시켜주자 - Comparable

 

public class People implements Comparable<People> {
    private String lastName; // 성
    private String firstName; // 이름
    private int age; //나이

    public People(String lastName, String firstName, int age) {
        this.lastName = lastName;
        this.firstName = firstName;
        this.age = age;
    }

    public String getLastName() {
        return this.lastName;
    }

    public String getFirstName() {
        return this.firstName;
    }

    public int getAge() {
        return this.age;
    }

    @Override
    public int compareTo(People o) {
        return o.getAge() - getAge();
    }
}

 

아까와 다르게 객체 People을 정의하는 과정에서 "implements Comparable <People>"이라는 부분이 추가된 것을 확인할 수가 있습니다.

 

implements Comparable이라는 부분은 객체에서 인터페이스 Comparable를 구현해주겠다는 것을 의미합니다. 이때, 인터페이스는 실질적인 구현 대신에 명세서처럼 메서드 선언을 가지고 있다는 것을 의미합니다.

인터페이스 Comparable을 implements 한 People은 Comparable이 추상적으로 가지고 있는 compareTo를 자신만의 기준에 맞게 구현하게 됩니다.

여기서 Comparable <People>에서 나오는 괄호 기호 <>은 자바의 기능 중 하나인 Generic으로써, 외부에서 타입을 정할 수 있게 해주는 기능입니다. 이때 저희는 <People>로 지정을 하였으니, Comparable라는 인터페이스 기준으로 한 외부의 관점에서 People이라는 타입을 사용하겠다!라는 것이 됩니다.

우리가 볼 수 있는 코드에서 다음의 부분인 

 

    @Override
    public int compareTo(People o) {
        return o.getAge() - getAge();
    }

 

이 부분이 compareTo를 오버 라이딩하여 실질적으로 구현한 부분입니다.

객체에서 비교할 다른 People인 o가 앞에 가게 되면 내림차순, 반대면 오름차순으로 정렬이 되도록 비교를 하게 됩니다.

 

 

나이의 내림 차순으로 비교되는 결과 값

 

또한, compareTo에서 우리가 원하는 데로 기준을 만들 수 있다는 점에 의하여, 어떤 필드를 비교할 기준으로 잡을지, 더 나아가서 String 타입을 가진 필드도 기준으로 잡아 정렬을 할 수가 있습니다.

 

객체를 비교할 수 있는 또 다른 객체를 만들어주자 - Comparator

객체 자체에 비교할 수 있는 기준을 만드는 Comparable과 다르게, Comparator는 특정 객체를 대상으로 비교를 할 수 있는 역할을 가지는 객체를 의미합니다.

이때, 이 객체는 마찬가지로 Generic 기능을 통해 외부에서 데이터 타입을 지정해주게 되며, 여기는 new Comparator <People>로 구현이 되었습니다.

 

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Application {
    public static void main(String args[]) {
        People people1 = new People("Ga", "Ga", 10);
        People people2 = new People("Na", "Na", 20);
        People people3 = new People("Da", "Da", 30);

        List<People> peoples = new ArrayList<>();
        peoples.add(people1);
        peoples.add(people2);
        peoples.add(people3);

        Comparator<People> comparator = new Comparator<People>() {
            @Override
            public int compare(People o1, People o2) {
                return o2.getAge() - o1.getAge();
            }
        };

        //Collcections.sorts(peoples, comparator);
        peoples.sort(comparator);

        for (People people : peoples){
            System.out.println(people.getLastName() + " "+ people.getAge());
        }
    }
}

 

 

여기서 Comparator라는 객체를 가져와서 구현을 하였는데, 오버 라이딩을 통해서 compare라는 메서드를 재 구현한 것을 알 수가 있습니다. 

 

 

Comparator<People> comparator = new Comparator<People>() {
  @Override
  public int compare(People o1, People o2) {
    return o2.getAge() - o1.getAge();
  }
};

 

이때, 내림 차순을 하기 위해서는 뒤의 People o2에서 앞의 People i1를 비교해주어야 하며 (o2.getAge() - o1.getAge())

오름 차순을 위해서는 앞의 People인 o1에서 뒤의 People o2를 빼주어야 합니다. (o1.getAge() - o2.getAge())

 

Comparator<People> comparator = (o1, o2) -> o2.getAge() - o1.getAge();

 

다음과 같이 람다식으로 깔끔하게도 표현이 가능합니다!

 

        //Collcections.sorts(peoples, comparator); //Comparator 사용
        peoples.sort(comparator); //List API 사용

 

Collections.sort()를 바로 List에 사용하던 Comparable과 달리 여기서는 Comparator를 List의 정렬 API인 sort()에 인자로 넣어주거나, Collection.sort에서 마찬 가지로 우리가 원하는 방향으로 정의된 Comparator를 추가해주면 됩니다.

+ Recent posts