迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > Java >

修复 Java 中的比较方法违反其一般契约错误

作者:迹忆客 最近更新:2023/07/12 浏览次数:

本篇文章我们将了解 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 < bb < c,那么对于任何三个数字 a、b、c,都意味着 a < c
  • 第三条规则是关于反对称的,其中 a < b 意味着 ~b < a
  • 可替代性也是一种比较规则,假设 a == ba < 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); 那么,我们将无法确定问题是什么以及问题发生在哪里。 这就是为什么我们通过日志记录对其进行排序,这将有助于我们识别它。

它给了我们一个错误,但它也提供了很多数字的比较。 我们不会讨论全部,但其中的几个就足以发现错误。

输出:

修复java中比较方法违反其一般契约错误 - 错误

我们可以看到,程序在这里违反了一致性规则,导致了这个错误。 在下一节中,我们将使用 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

这段代码执行成功。 我们不会进行所有的比较,但我们会看到其中的一些进行确认。

检查以下屏幕截图。

输出:

修复java中比较方法违反其一般契约错误 - 解决方案1

让我们了解一下使用 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==ba<ba>b,则分别返回 0、-1 和 1。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

如何在 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 中互斥锁的一切,在计算机科学领域,互斥或互斥被称为并发控制的属性。每台计算机都使用称为线程的最小程序指令序列。有一次,计算机在一个线程上工作。为了更好地理解,

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便