修复 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 中延迟几秒钟的时间
发布时间:2023/12/17 浏览次数:217 分类:Java
-
本篇文章主要介绍如何在 Java 中制造程序延迟。本教程介绍了如何在 Java 中制造程序延时,并列举了一些示例代码来了解它。
如何在 Java 中把 Hashmap 转换为 JSON 对象
发布时间:2023/12/17 浏览次数:187 分类:Java
-
它描述了允许我们将哈希图转换为简单的 JSON 对象的方法。本文介绍了在 Java 中把 Hashmap 转换为 JSON 对象的方法。我们将看到关于创建一个 hashmap,然后将其转换为 JSON 对象的详细例子。
如何在 Java 中按值排序 Map
发布时间:2023/12/17 浏览次数:171 分类:Java
-
本文介绍了如何在 Java 中按值对 Map 进行排序。本教程介绍了如何在 Java 中按值对 Map
进行排序,并列出了一些示例代码来理解它。
如何在 Java 中打印 HashMap
发布时间:2023/12/17 浏览次数:192 分类:Java
-
本帖介绍了如何在 Java 中打印 HashMap。本教程介绍了如何在 Java 中打印 HashMap 元素,还列举了一些示例代码来理解这个主题。
在 Java 中更新 Hashmap 的值
发布时间:2023/12/17 浏览次数:146 分类:Java
-
本文介绍了如何在 Java 中更新 HashMap 中的一个值。本文介绍了如何在 Java 中使用 HashMap 类中包含的两个方法-put() 和 replace() 更新 HashMap 中的值。
Java 中的 hashmap 和 map 之间的区别
发布时间:2023/12/17 浏览次数:79 分类:Java
-
本文介绍了 Java 中的 hashmap 和 map 接口之间的区别。本教程介绍了 Java 中 Map 和 HashMap 之间的主要区别。在 Java 中,Map 是用于以键值对存储数据的接口,
在 Java 中获取用户主目录
发布时间:2023/12/17 浏览次数:218 分类:Java
-
这篇文章向你展示了如何在 Java 中获取用户主目录。本教程介绍了如何在 Java 中获取用户主目录,并列出了一些示例代码以指导你完成该主题。
Java 中 size 和 length 的区别
发布时间:2023/12/17 浏览次数:179 分类:Java
-
这篇文章教你如何知道 Java 中大小和长度之间的区别。本教程介绍了 Java 中大小和长度之间的区别。我们还列出了一些示例代码以帮助你理解该主题。
Java 中的互斥锁
发布时间:2023/12/17 浏览次数:111 分类:Java
-
了解有关 Java 中互斥锁的一切,在计算机科学领域,互斥或互斥被称为并发控制的属性。每台计算机都使用称为线程的最小程序指令序列。有一次,计算机在一个线程上工作。为了更好地理解,