핵심 Java: Comparator 및 Comparable

35600 단어 corejava
사용자 정의 대상을 처리할 때, 우리는 일반적으로 일부 미리 정의된 표준에 따라 그것들을 비교하기를 원한다.이것이 바로 우리가 Comparable 인터페이스를 사용하여 사용자 정의 대상의 자연 정렬을 실현하는 이유이다.
또한 맞춤형 대상이 자연 순서를 실현하지 못하거나 이 대상이 정의한 자연 순서를 덮어쓰면 우리는 보통 총 순서를 지정해야 하기 때문에 우리는 Comparator 인터페이스를 사용하여 이런 일을 완성한다.

2. 예제 프레젠테이션
예시 시범에서 우리는 Person 인터페이스로 이루어진 자연 순서를 어떻게 사용하여 Comparable 대상을 비교하고 Comparator 인터페이스로 이루어진 새로운 순서를 어떻게 사용하여 이 자연 순서를 덮어쓰는지 보여줄 것이다.
다음과 같이 Person 클래스가 있다고 가정합니다.
class Person {
    private int age;
    private String firstName;
    private String lastName;

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

    // getters and setters

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
}

3. 비교 가능 커넥터

3.1 정의
사용자 정의 대상에서 ComparablecompareTo(T o) 방법을 실현함으로써 대상의 자연 순서를 정의할 수 있다.예를 들어 만약에 우리가 이름에 따라 Person 대상을 비교하고자 한다면 우리는 Comparable 인터페이스를 실현할 것이다. 다음과 같다.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Person implements Comparable<Person> {
    private int age;
    private String firstName;
    private String lastName;

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

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }

    @Override
    public int compareTo(Person person) {
        return this.firstName.compareTo(person.firstName);
    }

    public static void main(String[] args) {

        // people to sort
        List<Person> people = new ArrayList<>(List.of(
                new Person(20, "Hamza", "Belmellouki"),
                new Person(60, "Allan", "Truck"),
                new Person(40, "Zidan", "Kemero"),
                new Person(30, "Cindy", "Mahbd")
        ));
        people.forEach(System.out::println); //unsorted

        System.out.println("----------");

        Collections.sort(people);// sorted
        people.forEach(System.out::println);

    }
}
프로그램을 실행하면 출력은 다음과 같습니다.
Person{age=20, firstName=’Hamza’, lastName=’Belmellouki’}
Person{age=60, firstName=’Allan’, lastName=’Truck’}
Person{age=40, firstName=’Zidan’, lastName=’Kemero’}
Person{age=30, firstName=’Cindy’, lastName=’Mahbd’}
 — — — — — 
Person{age=60, firstName=’Allan’, lastName=’Truck’}
Person{age=30, firstName=’Cindy’, lastName=’Mahbd’}
Person{age=20, firstName=’Hamza’, lastName=’Belmellouki’}
Person{age=40, firstName=’Zidan’, lastName=’Kemero’}
보시다시피 compareTo 방법은 사전 순서를 바탕으로 하는 정렬 전략을 실현했습니다.이것은 compareTo 클래스에 정의된 String 를 호출하여 이 클래스는 사전 순서에 따라 두 문자열을 비교합니다.compareToperson.firstName와 같으면 우리this.fisrtName는 0을 되돌려주고, this.firstName가 사전에서 person.firstName보다 작으면 0보다 작은 값을 되돌려준다.this.fisrtName 사전에서 문자열 인자보다 크면, 이 값은 0보다 크다.
주의Collections.sort 방법은 인원 목록을 받아들이고 자연 순서에 따라 인원 목록을 정렬한다.
만약Person류가 실현되지 않았다면Comparable 컴파일할 때 오류가 발생할 수 있습니다. Collections.sort 방법이 범용 유형 파라미터를 정의하여 인터페이스를 확장했기 때문입니다. Comparable 인터페이스는 범용 유형 안전성이 우리가 벗어날 수 있도록 도와주는 것ClassCastException입니다!이것은 다른 주제이며 아마도 다른 블로그일 것이다.

3.2 비교 가능한 제품을 사용하는 모범 사례:
다음은 Comparable 사용 시 모범 사례입니다.
때때로 compareTo 방법은 다음과 같은 사실에 의존할 수 있습니다. 첫 번째 값이 두 번째 값보다 작으면 두 값 사이의 차이는 마이너스이고, 두 값이 같으면 0이며, 첫 번째 값이 더 크면 플러스입니다.예를 들어, 이 코드는 방법 약정compareTo 위반(전송성):
@Override
public int compareTo(Person p) {
    return this.age - p.age;
}
이런 기교를 쓰지 마라.그것은 정수 넘침과 IEEE 754 부점 산술 위영의 위험으로 가득 차 있다.
반대로 정적 정수, 이중 정밀도, 부동점compare 방법을 시종 사용한다.예를 들면 다음과 같습니다.
@Override
public int compareTo(Person p) {
    return Integer.compare(this.age, p.age);
}

4. 비교기 인터페이스

4.1 정의
만약 우리가 자연 정렬이 없는 대상에 총 정렬을 정의하거나, 자연 정렬을 덮어쓰고 싶다면 Comparator 인터페이스를 이용해서 실현할 수 있다.예를 들어 Person 대상의 자연 순서를 덮어쓰고 나이에 따라 Person 대상을 비교하려면 Comparator 인터페이스를 다음과 같이 실현할 것이다.
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class Person implements Comparable<Person> {
    private int age;
    private String firstName;
    private String lastName;

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

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }

    @Override
    public int compareTo(Person person) {
        return this.firstName.compareTo(person.firstName);
    }

    public static void main(String[] args) {

        // people to sort
        List<Person> people = new ArrayList<>(List.of(
                new Person(20, "Hamza", "Belmellouki"),
                new Person(60, "Allan", "Truck"),
                new Person(40, "Zidan", "Kemero"),
                new Person(30, "Cindy", "Mahbd")
        ));
        people.forEach(System.out::println); //unsorted

        System.out.println("----------");

        Comparator<Person> cmp = (o1, o2) -> Integer.compare(o1.age, o2.age);

        people.sort(cmp); // sorted

        people.forEach(System.out::println);
    }
}
이 코드를 실행하면 출력은 다음과 같습니다.
Person{age=20, firstName=’Hamza’, lastName=’Belmellouki’}
Person{age=60, firstName=’Allan’, lastName=’Truck’}
Person{age=40, firstName=’Zidan’, lastName=’Kemero’}
Person{age=30, firstName=’Cindy’, lastName=’Mahbd’}
 — — — — — 
Person{age=20, firstName=’Hamza’, lastName=’Belmellouki’}
Person{age=30, firstName=’Cindy’, lastName=’Mahbd’}
Person{age=40, firstName=’Zidan’, lastName=’Kemero’}
Person{age=60, firstName=’Allan’, lastName=’Truck’}
목록은 나이순으로 정렬되어 있으니 주의하세요.43줄에서, 우리는 lambda를 사용하여 compare 인터페이스에서 단일 추상적 Comparator 방법을 실현한 것을 볼 수 있습니다.우리는 Comparator#compare 방법의 실현에 대해 두 사람의 대상을 채택하고 o1.ageo2.age보다 작으면 마이너스로 돌아간다.만약 그것들이 같다면 0이다.의 정수o1.ageo2.age보다 큽니다.
45행에서 나는 List#sort 방법을 호출했는데 이 방법은 우리가 전달한 Comparator에 따라 목록을 정렬한다.

4.2 비교기 사용 시 모범 사례:
다음은 Comparator 사용 시 모범 사례입니다.Comparable 인터페이스는 다음과 같은 방법으로 사용할 수 있습니다. 객체를 비교하는 데 차를 사용하지 않고 항상 정적 정수, 이중 정밀도, 부동점compare 방법에 의존하여 수치를 비교합니다.
Comparator<Person> comparator = (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge());
그렇지 않으면 정수 넘침과 IEEE 754 부점 산술 위상을 초래할 수 있다.
두 번째 가장 좋은 실천은 대상이 지난번 비교에서 같다면 다른 비교 결과의if-else 문장을 연결하는 것을 피하는 것이다.예를 들어 설명하기 위해서 이 코드 세션에서 우리는 Comparator을 실현했다. 이것은 먼저 개인 대상의 이름에 따라 그것들을 비교하고 만약 그것들이 같다면 우리는 성씨로 이동한 다음에 나이로 이동할 것이다.
public static void main(String[] args) {

        // people to sort
        List<Person> people = new ArrayList<>(List.of(
                new Person(20, "Hamza", "Belmellouki"),
                new Person(50, "Hamza", "Belmellouki"),
                new Person(44, "Hamza", "Belmellouki"),
                new Person(60, "Allan", "Truck"),
                new Person(40, "Zidan", "Kemero"),
                new Person(30, "Cindy", "Mahbd")
        ));
        people.forEach(System.out::println); //unsorted

        System.out.println("----------");

        // Don't do this!
        Comparator<Person> cmp = (p1, p2) -> {
          int result = p1.firstName.compareTo(p2.firstName);
            if (result == 0) {
                result = p1.lastName.compareTo(p2.lastName);
                if (result == 0) {
                    return Integer.compare(p1.age, p2.age);
                } else {
                    return result;
                }
            } else {
                return result;
            }
        };

        people.sort(cmp); // sorted

        people.forEach(System.out::println);

    }
}
출력:
Person{age=20, firstName='Hamza', lastName='Belmellouki'}
Person{age=50, firstName='Hamza', lastName='Belmellouki'}
Person{age=44, firstName='Hamza', lastName='Belmellouki'}
Person{age=60, firstName='Allan', lastName='Truck'}
Person{age=40, firstName='Zidan', lastName='Kemero'}
Person{age=30, firstName='Cindy', lastName='Mahbd'}
----------
Person{age=60, firstName='Allan', lastName='Truck'}
Person{age=30, firstName='Cindy', lastName='Mahbd'}
Person{age=20, firstName='Hamza', lastName='Belmellouki'}
Person{age=44, firstName='Hamza', lastName='Belmellouki'}
Person{age=50, firstName='Hamza', lastName='Belmellouki'}
Person{age=40, firstName='Zidan', lastName='Kemero'}
이러한 비교를 하려고 할 때, 읽기가 어렵고, 틀리기 쉬우며, 건장성이 부족하기 때문에, 이런 낡은 패턴을 사용할 필요가 없다.대신 Java 8 ComparatorcomparingthenComparingXXX 메서드를 사용할 수 있습니다.
public static void main(String[] args) {

        // people to sort
        List<Person> people = new ArrayList<>(List.of(
                new Person(20, "Hamza", "Belmellouki"),
                new Person(50, "Hamza", "Belmellouki"),
                new Person(44, "Hamza", "Belmellouki"),
                new Person(60, "Allan", "Truck"),
                new Person(40, "Zidan", "Kemero"),
                new Person(30, "Cindy", "Mahbd")
        ));
        people.forEach(System.out::println); //unsorted

        System.out.println("----------");

        Comparator<Person> cmp = Comparator.comparing(Person::getFirstName)
                .thenComparing(Person::getLastName)
                .thenComparingInt(Person::getAge);

        people.sort(cmp); // sorted

        people.forEach(System.out::println);

}
이 코드도 같은 일을 했지만 읽을 수 있고 틀리기 쉽다.16줄에서 정적 비교기comparing 방법은 키 추출기를 사용하는데 이것은 Function 대상이 실현하여 비교 가능한 정렬 키를 추출하는 데 사용된다.
18번째 줄에서 나는 thenComparingInt 대신 thenComparing를 사용했다. 전자는 ToIntFunction 대상을 받아들였기 때문에 포장과 관련이 없다. 왜냐하면 추상적인 방법은 포장된 int가 아닌 int 원어로 되돌아오기 때문이다.

5.비교가능
기본 주문서를 원할 때 비교 가능한 인터페이스를 사용해야 합니다.유사한 인터페이스를 사용할 때, 우리는 클라이언트에서 어떠한 코드 변경도 할 필요가 없다.예를 들어 Collections#sort 방법은 자동으로 클래스compareTo() 방법을 사용한다.Comparator의 경우 클라이언트는 compare() 방법에서 사용하는 Comparator 클래스를 제공해야 합니다.
두 인터페이스 모두 Java 컬렉션 프레임워크의 일부입니다.
비교기를 사용하는 이유는 비교할 수 있는 구현 시나리오가 이미 있는 경우에도 몇 가지가 있습니다.
  • Compariable
  • 사용 시 사용할 수 없는 다양한 비교 정책 지정
  • 사용자 정의 클래스의 코드 변경/추가 방지
  • 일반적으로 대상을 정렬할 클래스의 원본 코드를 수정할 수 없기 때문에 이런 상황에서Comparable를 사용할 수 없습니다.

  • 7. 결론
    본고에서, 우리는Comparable를 어떻게 사용하여 자연 정렬을 실현하고, 비교기를 사용하여 그것을 덮어쓰는지 이해했다.우리는 실시할 때 모두가 알고 있는 가장 좋은 실천을 보았다.다음 기사에서는 더 많은 핵심 자바를 소개할 것이다.기대해주세요!
    아래의 댓글에서 당신의 생각을 알려주세요. 공유하는 것을 잊지 마세요!

    좋은 웹페이지 즐겨찾기