修复 Java 中的比较方法违反其一般契约错误
本篇文章我们将了解 Comparator 和 Comparable 接口使用的比较规则,这将导致Java中比较方法违反其一般契约错误的可能原因。 之后,我们将了解使用 Comparator 和 Comparable 接口的两种解决方案。
Comparator 和 Comparable 接口之间的区别
这是Java核心库中的两个接口。 比较器是一个比较两个不同对象并且独立于其他任何东西的函数。
它只查找两个输入,继续处理它们,并呈现结果。
另一方面,Comparable 是我们与数据类混合的接口。 例如,我们有一个包含一些数据的类; Comparable 接口将用于比较该类的第一个对象与同一类的第二个对象。
这意味着 Comparable 接口表明该实例可以与同一类的另一个实例进行比较。 请记住,Comparable 定义了类的自然顺序。
在其他方面,它与 Comparator 一样也是一个比较函数,并且在返回值、传递、自反等方面具有相同的规则。
Comparator 和 Comparable 的比较规则
比较方法违反了其一般契约,这意味着 Comparator 或 Comparable(基于我们正在使用的)有错误并违反了一致性规则之一。 一致性规则是什么?
下面我们就来学习一下它们。
我们假设我们已经为整数类型值编写了 Java 程序。 因此,我们的比较函数必须遵守以下规则。
-
给定任意两个整数 a 和 b,必须满足三分法,这意味着以下关系之一必须为真:
-
a 小于 b(如果
a < b
,则返回 -ve 值) -
a 等于 b (如果
a == b
则返回 0) -
a 大于 b(如果
a > b
,则返回 +ve 值)
-
a 小于 b(如果
-
它必须满足传递性,这意味着如果
a < b
且b < c
,那么对于任何三个数字 a、b、c,都意味着a < c
。 -
第三条规则是关于反对称的,其中
a < b
意味着~b < a
-
可替代性也是一种比较规则,假设
a == b
且a < c
; 这意味着b < c
。 -
最终的比较规则是自反性,其中
a == a
; 也~a < a
。
如果违反任何这些规则,我们会收到错误消息,指出比较方法违反了其一般契约。 让我们借助下面的代码示例来学习它。
Java代码具有比较方法违反了其一般契约错误
示例代码:
//import libraries
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import static java.util.stream.Collectors.toCollection;
//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 java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import static java.util.stream.Collectors.toCollection;
//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
在此代码中,如果两个对象的姓氏相同,我们将比较名字的字母数。
正如我们所看到的,结果是一致的。 如果 a==b
、a<b
和 a>b
,则分别返回 0、-1 和 1。
相关文章
修复 Java 中错误 Error: Else Without if
发布时间:2023/07/13 浏览次数:183 分类:Java
-
本篇文章我们将了解在用 Java 编写代码时只说 'else' without 'if' 的错误。 我们还将找出导致此错误的可能原因并找到解决方案。Java 中 error: 'else' without 'if'
Java 中 The System Cannot Find the File Specified
发布时间:2023/07/13 浏览次数:99 分类:Java
-
本篇文章介绍如何解决 Java 中的 The system cannot find the file specified 错误。修复Java中 The system cannot find the file specified 错误
Java 中的无效字符常量
发布时间:2023/07/13 浏览次数:152 分类:Java
-
本 Java 文章将讨论无效字符常量。 但在此之前,我们需要了解字符常量。Java字符常量 单字符常量,也称为字符常量,是封装在一对 '' 或单引号中的单个字符。
解决 Java 中 Missing Method Body or Declare Abstract
发布时间:2023/07/13 浏览次数:68 分类:Java
-
本篇文章讨论编译时错误、missing method body, or declare abstract。 在这里,我们将经历三个不同的步骤。首先,我们将了解一个Java程序来了解错误。 其次,突出显示此错误的可能原因,并最终找到
在 Java 中使用 Scanner 时没有此类元素异常
发布时间:2023/07/13 浏览次数:72 分类:Java
-
本篇文章将介绍如何在 Java 中使用 Scanner 时解决 NoSuchElementException 错误。在 Java 中使用 Scanner 时没有此类元素异常 Scanner 类用于在 Java 程序中获取用户输入。 它使用多种实用方法,如 next()、
Java 错误 Javac Is Not Recognized as an Internal or External Command, Operable
发布时间:2023/07/13 浏览次数:108 分类:Java
-
本文介绍了 Java 的 'javac' is not recognized as an internal or external command, operable program or batch file 的可能原因以及该错误的可能解决方案。 首先,我们来看看 javac 是什么。Java 中的 Javac
Java 错误 Java.Net.SocketTimeoutException: Connection Timed Out
发布时间:2023/07/13 浏览次数:161 分类:Java
-
在本篇文章中,我们将讨论 java.net.SocketTimeoutException: Connection timed out。 但首先,让我们仔细看看套接字和超时的概念。Java 中的套接字 两个计算机应用程序之间的逻辑链接可能有多个端点,其
Java 错误 Char Cannot Be Dereferenced
发布时间:2023/07/13 浏览次数:87 分类:Java
-
本篇文章介绍如何解决Java的 java char cannot be dereferenced 错误。Java Char cannot be dereferenced 当我们尝试使用 equals() 方法检查一个字符是否与另一个字符相等时,会出现错误 java char can not be dereferenc
JavaFX 中 InvocationTargetException
发布时间:2023/07/13 浏览次数:165 分类:Java
-
本篇文章介绍如何解决 JavaFX 中的 InvocableTargetException。修复 JavaFX 中的 InspirationTargetException 当我们使用 JavaFX 时,会发生 InvokingTargetException 异常。