수정 비교 방법이 Java의 일반 계약 오류를 위반함
-
Comparator
와Comparable
인터페이스의 차이점 -
Comparator
및Comparable
에 대한 비교 규칙 -
비교 방법이 일반 계약을 위반함
오류가 있는 Java 코드 -
Comparator
및Comparable
인터페이스를 사용하는 Java 솔루션
오늘은 Comparator
및 Comparable
인터페이스에서 사용하는 비교 규칙에 대해 알아보겠습니다. 이로 인해 Java에서 comparison 메서드가 일반 계약을 위반함
오류가 발생할 수 있습니다. 그런 다음 Comparator
및 Comparable
인터페이스를 사용하는 두 가지 솔루션을 이해합니다.
Comparator
와 Comparable
인터페이스의 차이점
이들은 Java 핵심 라이브러리의 두 인터페이스입니다. 비교기
는 서로 다른 두 개체를 비교하는 기능이며 다른 것과는 독립적입니다.
두 개의 입력만 찾고 프로세스를 계속 진행한 다음 결과를 표시합니다.
반면 Comparable
은 데이터 클래스와 혼합되는 인터페이스입니다. 예를 들어, 일부 데이터가 있는 클래스가 있습니다. Comparable
인터페이스는 이 클래스의 첫 번째 개체를 같은 클래스의 두 번째 개체와 비교하는 데 사용됩니다.
이는 Comparable
인터페이스가 이
인스턴스를 동일한 클래스의 다른 인스턴스와 비교할 수 있음을 나타냅니다. Comparable
은 클래스의 natural
순서를 정의합니다.
다른 측면에서는 Comparator
와 같은 비교 함수이기도 하며 반환 값, 전이적, 재귀적 등에 대해 동일한 규칙을 가집니다.
Comparator
및 Comparable
에 대한 비교 규칙
비교 방법이 일반 계약을 위반함
은 비교기
또는 비교 가능
(우리가 사용하는 것에 따라)에 버그가 있고 일관성 규칙 중 하나를 위반함을 의미합니다. 일관성 규칙은 무엇입니까?
아래에서 배워봅시다.
정수 유형 값에 대한 Java 프로그램을 작성했다고 가정합니다. 따라서 비교 함수는 다음 규칙을 준수해야 합니다.
-
임의의 두 정수
a
및b
가 주어지면 삼분법이 충족되어야 합니다. 즉, 다음 관계 중 정확히 하나가 참이어야 합니다.a
는b
보다 작습니다(a < b
인 경우-ve
값 반환)a
는b
와 같습니다(a == b
인 경우0
을 반환함)a
는b
보다 큽니다(a > b
인 경우+ve
값 반환)
-
전이성을 만족해야 합니다. 즉,
a < b
및b < c
인 경우 세 숫자a
,b
,c
에 대해a < c
를 의미합니다. -
세 번째 규칙은 Antisymmetry에 관한 것입니다. 여기서
a < b
는~b < a
를 의미합니다. -
대체 가능성은
a == b
및a < c
라고 가정하는 비교 규칙이기도 합니다. 이것은b < c
를 의미합니다. -
최종 비교 규칙은 반사성입니다. 여기서
a == a
입니다. 또한~a < a
.
이러한 규칙 중 하나라도 위반되면 비교 방법이 일반 계약을 위반함
이라는 오류가 발생합니다. 아래 코드 예제를 통해 배워봅시다.
비교 방법이 일반 계약을 위반함
오류가 있는 Java 코드
예제 코드:
// import libraries
import static java.util.stream.Collectors.toCollection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
// Main
public class Main {
public static void main(String[] args) {
// generate random numbers
List<Integer> list = new Random(209).ints(32L).boxed().collect(toCollection(ArrayList::new));
// sort using lambda expression
list.sort(logging((a, b) -> a - b));
} // end main()
// logging the comparisons
static Comparator<Integer> logging(Comparator<Integer> c) {
return (a, b) -> {
int r = c.compare(a, b);
System.err.printf("%,14d %,14d => %,14d\n", a, b, r);
return r;
};
} // end logging()
} // end class
이 코드에서는 난수 스트림을 생성하는 데 사용되는 Random
클래스의 ints
인스턴스를 사용하여 몇 가지 난수를 생성합니다.
이 코드의 경우 list.sort((a, b) -> a - b);
로 정렬하면 그러면 문제가 무엇이며 어디에서 발생하는지 확인할 수 없습니다. 이것이 우리가 그것을 식별하는 데 도움이 될 로깅
을 통해 정렬하는 이유입니다.
그것은 우리에게 오류를 주지만, 그것은 또한 많은 수의 비교를 제공합니다. 모두 다루지는 않겠지만 그 중 몇 가지는 오류를 찾기에 충분합니다.
출력:
보시다시피 프로그램은 여기서 일관성 규칙을 위반하여 이 오류가 발생합니다. 다음 섹션에서는 Comparator
및 Comparable
을 사용하여 솔루션을 살펴보겠습니다.
Comparator
및 Comparable
인터페이스를 사용하는 Java 솔루션
난수가 적은 Comparator
인터페이스를 사용해 봅시다.
예제 코드:
// import libraries
import static java.util.stream.Collectors.toCollection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
// Main
public class Main {
public static void main(String[] args) {
// generate random numbers
List<Integer> list = new Random(5).ints(32L).boxed().collect(toCollection(ArrayList::new));
// sort using lambda expression
list.sort(logging((a, b) -> a - b));
} // end main()
// logging the comparisons
static Comparator<Integer> logging(Comparator<Integer> c) {
return (a, b) -> {
int r = c.compare(a, b);
System.err.printf("%,14d %,14d => %,14d\n", a, b, r);
return r;
};
} // end logging()
} // end class
이 코드는 성공적으로 실행됩니다. 우리는 모든 비교를 거치지는 않지만 확인을 위해 몇 가지 비교를 볼 것입니다.
다음 스크린샷을 확인하십시오.
출력:
비교를 위한 Comparable
인터페이스를 사용하여 오류가 없는 Java 코드에 대해 알아봅시다. 이 인터페이스를 사용하려면 데이터를 포함하는 클래스를 만들어야 합니다.
아래에서 해봅시다.
예제 코드(Students.java
클래스):
public class Student implements Comparable<Student> {
private String firstName;
private String lastName;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int compareTo(Student other) {
/*
compare the last names and save the result
in `compareResult` variable
*/
int compareResult = this.lastName.compareTo(other.lastName);
/*
If both last names match, then it would be true means 0. So,
dive into the `if` condition and check if the count of their
first name matches.
if this.count == other.count return 0
if this.count > other.count return 1
if this.count < other.count return -1
*/
if (compareResult == 0) {
if (this.firstName.chars().count() == other.firstName.chars().count()) {
compareResult = 0;
return compareResult;
} else if (this.firstName.chars().count() > other.firstName.chars().count()) {
compareResult = 1;
return compareResult;
} else {
compareResult = -1;
return compareResult;
}
} else {
return compareResult;
}
}
}
예제 코드(Main.java
클래스):
public class Main {
public static void main(String[] args) {
Student std1 = new Student("Mehvish", "Ashiq");
Student std2 = new Student("Mehvish", "Ashiq");
System.out.println("Result of Comparison 1: " + std1.compareTo(std2));
Student std3 = new Student("Aftab", "Ashiq");
Student std4 = new Student("Mehvish", "Ashiq");
System.out.println("Result of Comparison 2: " + std3.compareTo(std4));
Student std5 = new Student("Mehr-un-nissa", "Ashiq");
Student std6 = new Student("Mehvish", "Ashiq");
System.out.println("Result of Comparison 3: " + std5.compareTo(std6));
}
}
출력:
Result of Comparison 1: 0
Result of Comparison 2: -1
Result of Comparison 3: 1
이 코드에서는 두 개체의 lastName
이 동일한 경우 firstName
의 문자 수를 비교합니다.
보시다시피 결과는 일관됩니다. a==b
, a<b
및 a>b
인 경우 각각 0
, -1
및 1
을 반환합니다.