Java Language
비교 가능 및 비교기
수색…
통사론
- 공용 클래스 MyClass는 Comparable
<MyClass
> - 공용 클래스 MyComparator는 Comparator
<SomeOtherClass
> - 공용 int compareTo (MyClass 기타)
- 공용 int compare (SomeOtherClass o1, SomeOtherClass o2)
비고
double
의존하는 compareTo(..)
메서드를 구현할 때는 다음을 수행하지 마십시오.
public int comareTo(MyClass other) {
return (int)(doubleField - other.doubleField); //THIS IS BAD
}
(int)
인한 잘림은 메서드가 양수 또는 음수 대신 0
잘못 반환하는 경우가 발생하므로 비교 및 정렬 버그가 발생할 수 있습니다.
대신 Double.compare를 다음 과 같이 사용하는 것이 가장 간단합니다.
public int comareTo(MyClass other) {
return Double.compare(doubleField,other.doubleField); //THIS IS GOOD
}
의 제네릭이 아닌 버전은 Comparable<T>
, 단순히 Comparable
, 자바 1.2부터 존재했다 . 레거시 코드와 인터페이싱하는 것 이외에, 비교시 캐스팅 할 필요가 없기 때문에 제네릭 버전 인 Comparable<T>
을 구현하는 것이 항상 더 좋습니다.
그것은 다음과 같이 클래스가 자신과 비교할 수있는 표준입니다.
public class A implements Comparable<A>
이 패러다임에서 벗어날 수는 있지만 그렇게 할 때는주의해야합니다.
그 클래스가 Comparable<T>
구현하고있는 경우, Comparator<T>
는 클래스의 인스턴스로 계속 사용할 수 있습니다. 이 경우 Comparator
의 로직이 사용됩니다. Comparable
구현에 의해 지정된 자연 순서 부가 무시됩니다.
Comparable을 사용하여 목록 정렬 또는 비교기
Person을 대표하는 클래스를 성과 이름으로 작업한다고 가정 해 보겠습니다. 우리는 이것을 수행하고 적절한 equals
및 hashCode
메소드를 구현하기위한 기본 클래스를 작성했습니다.
public class Person {
private final String lastName; //invariant - nonnull
private final String firstName; //invariant - nonnull
public Person(String firstName, String lastName){
this.firstName = firstName != null ? firstName : "";
this.lastName = lastName != null ? lastName : "";
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String toString() {
return lastName + ", " + firstName;
}
@Override
public boolean equals(Object o) {
if (! (o instanceof Person)) return false;
Person p = (Person)o;
return firstName.equals(p.firstName) && lastName.equals(p.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
}
이제 다음과 같은 경우와 같이 이름으로 Person
객체 목록을 정렬하려고합니다.
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //This currently won't work.
}
불행히도, 표시된대로, 위의 현재 컴파일되지 않습니다. Collections.sort(..)
는 목록의 요소가 비교 가능하거나 비교의 사용자 지정 메서드가 제공되는 경우에만 목록을 정렬하는 방법을 알고 있습니다.
다음 목록을 정렬하라는 요청을 받았다면 : 1,3,5,4,2
, 대답은 1,2,3,4,5
라고 말할 수 있습니다. 이는 정수 (Java 및 수학 모두)가 자연 순서 (natural ordering ), 표준 기본 비교 기본 순서를 가지기 때문입니다. Person 클래스에 자연스러운 순서를 부여하기 위해 compareTo(Person p):
메소드를 구현해야하는 Comparable<Person>
구현합니다 compareTo(Person p):
public class Person implements Comparable<Person> {
private final String lastName; //invariant - nonnull
private final String firstName; //invariant - nonnull
public Person(String firstName, String lastName) {
this.firstName = firstName != null ? firstName : "";
this.lastName = lastName != null ? lastName : "";
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String toString() {
return lastName + ", " + firstName;
}
@Override
public boolean equals(Object o) {
if (! (o instanceof Person)) return false;
Person p = (Person)o;
return firstName.equals(p.firstName) && lastName.equals(p.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
@Override
public int compareTo(Person other) {
// If this' lastName and other's lastName are not comparably equivalent,
// Compare this to other by comparing their last names.
// Otherwise, compare this to other by comparing their first names
int lastNameCompare = lastName.compareTo(other.lastName);
if (lastNameCompare != 0) {
return lastNameCompare;
} else {
return firstName.compareTo(other.firstName);
}
}
}
이제 주어진 주 방법이 올바르게 작동합니다.
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Now functions correctly
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}
그러나 클래스 Person
을 원하지 않거나 수정할 수없는 경우, 두 Person
오브젝트의 비교를 처리하는 사용자 정의 Comparator<T>
를 제공 할 수 있습니다. circle, square, rectangle, triangle, hexagon
목록을 정렬하라는 요청을 받았지만 모서리 수에 따라 목록을 정렬하도록 요청한 경우 그렇게 할 수 있습니다. 마찬가지로, 비교자를 제공하면 자바에게 비교할 수없는 두 객체를 비교하는 방법을 지시합니다.
public class PersonComparator implements Comparator<Person> {
public int compare(Person p1, Person p2) {
// If p1's lastName and p2's lastName are not comparably equivalent,
// Compare p1 to p2 by comparing their last names.
// Otherwise, compare p1 to p2 by comparing their first names
if (p1.getLastName().compareTo(p2.getLastName()) != 0) {
return p1.getLastName().compareTo(p2.getLastName());
} else {
return p1.getFirstName().compareTo(p2.getFirstName());
}
}
}
//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Illegal, Person doesn't implement Comparable.
Collections.sort(people, new PersonComparator()); //Legal
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}
Comparator는 익명의 내부 클래스로 생성 / 사용될 수도 있습니다.
//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Illegal, Person doesn't implement Comparable.
Collections.sort(people, new PersonComparator()); //Legal
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
//Anonymous Class
Collections.sort(people, new Comparator<Person>() { //Legal
public int compare(Person p1, Person p2) {
//Method code...
}
});
}
람다 식 기반 비교기
자바 8에서, 비교기는 또한 람다 표현식으로 표현 될 수있다.
//Lambda
Collections.sort(people, (p1, p2) -> { //Legal
//Method code....
});
Comparator의 디폴트 메소드
게다가 Comparator를 구현하기위한 Comparator 인터페이스에는 흥미로운 기본 메소드가 있습니다. 다음은 lastName
과 firstName
비교하는 비교기를 작성합니다.
Collections.sort(people, Comparator.comparing(Person::getLastName)
.thenComparing(Person::getFirstName));
비교기의 순서 반전
모든 비교자를 오름차순에서 내림차순으로 변경하는 reversedMethod
를 사용하여 쉽게 되돌릴 수 있습니다.
compareTo 및 compare 메소드
Comparable<T>
인터페이스에는 다음과 같은 메서드가 필요합니다.
public interface Comparable<T> {
public int compareTo(T other);
}
그리고 Comparator<T>
인터페이스에는 다음과 같은 한 가지 방법이 필요합니다.
public interface Comparator<T> {
public int compare(T t1, T t2);
}
:이 두 가지 방법은 하나 개의 작은 차이가 본질적으로 같은 일을 할 compareTo
비교 this
에 other
반면, compare
비교 t1
에 t2
하지 돌보는 모두에 대해 this
.
이 두 가지 방법을 제외하고는 비슷한 차이점이 있습니다. 특히 (compareTo의 경우), 이 오브젝트를 지정된 오브젝트와 비교해 순서 붙일 수 있습니다. 이 객체가 지정된 객체보다 작거나 같거나 큰 경우 음수, 0 또는 양의 정수를 반환합니다. 따라서, a
와 b
의 비교를 a
:
-
a < b
,a.compareTo(b)
및compare(a,b)
가 음의 정수를 반환하고b.compareTo(a)
및compare(b,a)
가 양의 정수를 반환해야하는 경우 -
a > b
,a.compareTo(b)
및compare(a,b)
가 양의 정수를 반환하고b.compareTo(a)
및compare(b,a)
가 음의 정수를 반환해야하는 경우 - 경우
a
동일b
비교를 위해, 모든 비교는 반환해야합니다0
.
자연 (비교 가능) 대 명시 (비교 자) 정렬
두 개의 Collections.sort()
메소드가 있습니다.
- 하나는
List<T>
를T
가 Comparable을 구현해야하는 매개 변수로 취하고 정렬 순서를 결정하는compareTo()
메서드를 재정의합니다. - Comparator가 정렬 순서를 결정하는 List 및 Comparator를 인수로 취하는 메서드
첫째, Comparable을 구현 한 Person 클래스가있다.
public class Person implements Comparable<Person> {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Person o) {
return this.getAge() - o.getAge();
}
@Override
public String toString() {
return this.getAge()+"-"+this.getName();
}
}
위의 클래스를 사용하여 compareTo()
메서드 재정의에 의해 정의 된 요소의 자연 순서로 목록을 정렬하는 방법은 다음과 같습니다.
//-- usage
List<Person> pList = new ArrayList<Person>();
Person p = new Person();
p.setName("A");
p.setAge(10);
pList.add(p);
p = new Person();
p.setName("Z");
p.setAge(20);
pList.add(p);
p = new Person();
p.setName("D");
p.setAge(30);
pList.add(p);
//-- natural sorting i.e comes with object implementation, by age
Collections.sort(pList);
System.out.println(pList);
다음은 익명 인라인 Comparator를 사용하여 Comparable을 구현하지 않는 List를 정렬하거나이 경우 자연 정렬과 다른 순서로 List를 정렬하는 방법입니다.
//-- explicit sorting, define sort on another property here goes with name
Collections.sort(pList, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(pList);
지도 항목 정렬
Java 8의 경우 map 반복을 정렬 할 수 있도록 Map.Entry
인터페이스에 기본 메소드가 있습니다.
Map<String, Integer> numberOfEmployees = new HashMap<>();
numberOfEmployees.put("executives", 10);
numberOfEmployees.put("human ressources", 32);
numberOfEmployees.put("accounting", 12);
numberOfEmployees.put("IT", 100);
// Output the smallest departement in terms of number of employees
numberOfEmployees.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.limit(1)
.forEach(System.out::println); // outputs : executives=10
물론 스트림 API 외부에서 사용할 수도 있습니다.
List<Map.Entry<String, Integer>> entries = new ArrayList<>(numberOfEmployees.entrySet());
Collections.sort(entries, Map.Entry.comparingByValue());
비교 메서드를 사용하여 Comparator 만들기
Comparator.comparing(Person::getName)
이것은이 사람 이름을 비교 소스로 사용하는 Person
클래스에 대한 비교자를 생성합니다. 또한 메서드 버전을 사용하여 long, int 및 double을 비교할 수 있습니다. 예 :
Comparator.comparingInt(Person::getAge)
역 주문
역순을 부과하는 컴퍼 레이터를 작성하려면, reversed()
메소드를 사용합니다.
Comparator.comparing(Person::getName).reversed()
비교기 체인
Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName)
이렇게하면 firs와 성을 비교 한 다음 이름과 비교하는 비교자를 생성합니다. 원하는만큼 많은 비교기를 연결할 수 있습니다.