数据结构之第十章、Java对象的比较

简介: 数据结构之第十章、Java对象的比较

 

目录

一、PriorityQueue(堆)中插入对象

二、元素的比较

2.1基本类型的比较

2.2对象比较的问题

三、对象的比较

3.1覆写基类的equals

3.2基于Comparble接口类的比较

3.3基于比较器比较

3.4三种方式对比

3.5代码实现

四、集合框架中PriorityQueue的比较方式

五、使用PriorityQueue创建大小堆,解决TOPK问题


一、PriorityQueue(堆)中插入对象

优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较,为了简单起见,我们只是插入了Integer类型,那优先级队列中能否插入自定义类型对象呢?

class Card {
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
}
public class TestPriorityQueue {
    public static void TestPriorityQueue() {
        //Card自定义类型-->优先级队列实现
        PriorityQueue<Card> p = new PriorityQueue<>();
        p.offer(new Card(1, "♠"));
        p.offer(new Card(2, "♠"));
    }
    public static void main(String[] args) {
        TestPriorityQueue();
    }
}

image.gif

优先级队列底层使用堆,而向堆中插入元素时,为了满足堆的性质,必须要进行元素的比较,而此时Card是没有办法直接进行比较的,因此抛出异常。

image.gif编辑


二、元素的比较

2.1基本类型的比较

在Java中,基本类型的对象可以直接比较大小。

public class TestCompare {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println(a > b);
        System.out.println(a < b);
        System.out.println(a == b);
        char c1 = 'A';
        char c2 = 'B';
        System.out.println(c1 > c2);
        System.out.println(c1 < c2);
        System.out.println(c1 == c2);
        boolean b1 = true;
        boolean b2 = false;
        System.out.println(b1 == b2);
        System.out.println(b1 != b2);
    }
}

image.gif

2.2对象比较的问题

class Card {
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
}
public class TestPriorityQueue {
    public static void main(String[] args) {
        Card c1 = new Card(1, "♠");
        Card c2 = new Card(2, "♠");
        Card c3 = c1;
//System.out.println(c1 > c2); // 编译报错
        System.out.println(c1 == c2); // 编译成功 ----> 打印false,因为c1和c2指向的是不同对象
//System.out.println(c1 < c2); // 编译报错
        System.out.println(c1 == c3); // 编译成功 ----> 打印true,因为c1和c3指向的是同一个对象
    }
}

image.gif

c1、c2和c3分别是Card类型的引用变量,上述代码在比较编译时:

c1 > c2 编译失败

c1== c2 编译成功

c1 < c2 编译失败

从编译结果可以看出,Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较。 那为什么==可以比较?因为:对于用户实现自定义类型,都默认继承自Object类,而Object类中提供了equal方法,而==默认情况下调用的就是equal方法,但是该方法的比较规则是:没有比较引用变量引用对象的内容,而是直接比较引用变量的地址,但有些情况下该种比较就不符合题意。

//源码
// Object中equal的实现,可以看到:直接比较的是两个引用变量的地址
public boolean equals(Object obj) {
    return (this == obj);
}

image.gif


三、对象的比较

向优先级队列中插入某个对象时,需要对按照对象中内容来调整堆,那该如何处理呢?

3.1覆写基类的equals

class Card {
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
    @Override
    public boolean equals(Object o) {
// 自己和自己比较
        if (this == o) {
            return true;
        }
// o如果是null对象,或者o不是Card的子类
        if (o == null || !(o instanceof Card)) {
            return false;
        }
// 注意基本类型可以直接比较,但引用类型最好调用其equal方法
        Card c = (Card)o;
        return rank == c.rank
                && suit.equals(c.suit);
    }
}

image.gif

注意:

1. 如果指向同一个对象,返回 true
2. 如果传入的为 null,返回 false
3. 如果传入的对象类型不是 Card,返回 false

4. 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌

5. 注意:调用其他引用类型的比较也需要 equals,例如这里的 suit 的比较

覆写基类equal的方式虽然可以比较,但缺陷是:equal只能按照相等进行比较,不能按照大于、小于的方式进行比较。

3.2基于Comparble接口类的比较

Comparble是JDK提供的泛型的比较接口类,源码实现具体如下:

public interface Comparable<E> {
    // 返回值:
    // < 0: 表示 this 指向的对象小于 o 指向的对象
    // == 0: 表示 this 指向的对象等于 o 指向的对象
    // > 0: 表示 this 指向的对象大于 o 指向的对象
    int compareTo(E o);
}

image.gif

对于用户自定义类型,如果要想按照大小的方式进行比较时:在定义类时,实现Comparble接口即可,然后在类中重写compareTo方法。

image.gif编辑

class Card implements Comparable<Card>{
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
    @Override
    public boolean equals(Object o) {
// 自己和自己比较
        if (this == o) {
            return true;
        }
// o如果是null对象,或者o不是Card的子类
        if (o == null || !(o instanceof Card)) {
            return false;
        }
// 注意基本类型可以直接比较,但引用类型最好调用其equal方法
        Card c = (Card)o;
        return rank == c.rank
                && suit.equals(c.suit);
    }
    // 根据数值比较,不管花色
// 这里我们认为 null 是最小的
    @Override
    public int compareTo(Card o) {
        if (o == null) {
            return 1;
        }
        return this.rank - o.rank;
    }
}
public class TestPriorityQueue {
    public static void main(String[] args){
        Card p = new Card(1, "♠");
        Card q = new Card(2, "♠");
        Card o = new Card(1, "♠");
        System.out.println(p.compareTo(o)); // == 0,表示牌相等
        System.out.println(p.compareTo(q)); // < 0,表示 p 比较小
        System.out.println(q.compareTo(p)); // > 0,表示 q 比较大
    }
}

image.gif

Compareble是java.lang中的接口类,可以直接使用。

3.3基于比较器比较

按照比较器方式进行比较,具体步骤如下:

用户自定义比较器类,实现Comparator接口

//源码如下
public interface Comparator<T> {
    // 返回值:
    // < 0: 表示 o1 指向的对象小于 o2 指向的对象
    // == 0: 表示 o1 指向的对象等于 o2 指向的对象
    // > 0: 表示 o1 指向的对象等于 o2 指向的对象
int compare(T o1, T o2);
}

image.gif

覆写Comparator中的compare方法:

class CardComparator implements Comparator<Card> {
    // 根据数值比较,不管花色
// 这里我们认为 null 是最小的
    @Override
    public int compare(Card o1, Card o2) {
        if (o1 == o2) {
            return 0;
        }
        if (o1 == null) {
            return -1;
        }if (o2 == null) {
            return 1;
        }
        return o1.rank - o2.rank;
    }
    public static void main(String[] args){
        Card p = new Card(1, "♠");
        Card q = new Card(2, "♠");
        Card o = new Card(1, "♠");
// 定义比较器对象
        CardComparator cmptor = new CardComparator();
// 使用比较器对象进行比较
        System.out.println(cmptor.compare(p, o)); // == 0,表示牌相等
        System.out.println(cmptor.compare(p, q)); // < 0,表示 p 比较小
        System.out.println(cmptor.compare(q, p)); // > 0,表示 q 比较大
    }
}

image.gif

注意:Comparator是java.util 包中的泛型接口类,使用时必须导入对应的包

3.4三种方式对比

image.gif编辑

3.5代码实现

//年龄的比较器
//基于比较器实现对象的比较(大于等于小于)
public class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student stu1,Student stu2){
        if(stu1==stu2){
            return 0;
        }
        if(stu1==null){
            return -1;
        }
        if(stu2==null){
            return 1;
        }
        return stu1.getAge()-stu2.getAge();
    }
}
//基于比较器实现对象的比较(大于等于小于)
public class NameComparator implements Comparator<Student>{
    @Override
    public int compare(Student stu1,Student stu2){
        if(stu1==stu2){
            return 0;
        }
        if(stu1==null){
            return -1;
        }
        if(stu2==null){
            return 1;
        }
        return stu1.getName().compareTo(stu2.getName());
    }
}
class Imp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
}
public class Student implements Comparable<Student>{
    private int age;
    private String name;
    public Student(){}
    public Student(int age,String name){
        this.age=age;
        this.name=name;
    }
    @Override
    public String toString(){
        return "["+"姓名:"+name+",年龄:"+age+"]";
    }
    //重写equals方法:只能实现相等的比较
    @Override
    public boolean equals(Object obj){
        if(this==obj){
            return true;
        }
        if(obj==null||!(obj instanceof Student)){
            return false;
        }
        Student stu=(Student)obj;
        return stu.age==this.age&&stu.name.equals(this.name);
    }
    @Override
    public int hashCode(){
        return Objects.hash(age,name);
    }
    //重写conpareTo方法
    @Override
    public int compareTo(Student stu){
        //return stu.age-this.age;
        if(stu.name==null){
            return 1;
        }
        return this.name.compareTo(stu.name);
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //实现多个对象的排序
    public static void bubbleSort(Comparable<Student>[] students){
        boolean flag=true;
        if(flag==true){
            flag=false;
            for (int i = 0; i < students.length-1; i++) {
                for (int j = 0; j < students.length-1-i; j++) {
                    if(students[j].compareTo((Student) students[j+1])>0){
                        swap(students,j,j+1);
                    }
                }
            }
        }
    }
    public static void swap(Comparable<Student>[] students,int i,int j){
        Comparable<Student> tmp=students[i];
        students[i]=students[j];
        students[j]=tmp;
    }
}
public class Test {
    public static void main(String[] args) {
        Student stu1=new Student(21,"J杰");
        Student stu2=new Student(19,"E杰");
        Student stu3=new Student(21,"B晓东");
        Student stu4=new Student(17,"C杰东");
        Student stu5=new Student(16,"U王");
        Student[] students={stu1,stu2,stu3,stu4,stu5};
        //实现对年龄的比较:
        AgeComparator ageComparator=new AgeComparator();
        System.out.println(ageComparator.compare(stu1, stu3));
        System.out.println(ageComparator.compare(stu1, stu2));
        System.out.println(stu1.compareTo(stu3));
        System.out.println(stu1.compareTo(stu2));
        //实现对姓名的比较:
        System.out.println("=========");
        NameComparator nameComparator=new NameComparator();
        System.out.println(nameComparator.compare(stu1, stu2));
        System.out.println(nameComparator.compare(stu1, stu3));
        //实现对整体的比较:
        System.out.println("=========");
        System.out.println(stu1.equals(stu2));
        System.out.println(stu1.equals(stu3));
        System.out.println("==========");
        System.out.println("对象实现冒泡排序");
        Student.bubbleSort(students);
        System.out.println(Arrays.deepToString(students));
        PriorityQueue<Student> priorityQueue = new PriorityQueue<>(nameComparator);
        priorityQueue.offer(s2);
        priorityQueue.offer(s1);
        Student ret = priorityQueue.poll();
        System.out.println(ret);
    }
}

image.gif


四、集合框架中PriorityQueue的比较方式

集合框架中的PriorityQueue底层使用堆结构,因此其内部的元素必须要能够比大小,PriorityQueue采用了:Comparble和Comparator两种方式。

1. Comparble是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparble接口,并覆写compareTo方法
2. 用户也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口并覆写compare方法。

image.gif编辑

image.gif编辑

image.gif编辑

image.gif编辑image.gif编辑


五、使用PriorityQueue创建大小堆,解决TOP-K问题

public class LessIntComp {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2;
    }
}
public class GreaterIntComp {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
}
public class TestDemo {
    //求最小的K个数,通过比较器创建大根堆
    public static int[] smallestK(int[] array, int k) {
        if(k <= 0) {
            return new int[k];
        }
        GreaterIntComp greaterCmp = new GreaterIntComp();
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(greaterCmp);
//先将前K个元素,创建大根堆
        for(int i = 0; i < k; i++) {
            maxHeap.offer(array[i]);
        }
//从第K+1个元素开始,每次和堆顶元素比较
        for (int i = k; i < array.length; i++) {
            int top = maxHeap.peek();
            if(array[i] < top) {
                maxHeap.poll();
                maxHeap.offer(array[i]);
            }
        }
//取出前K个
        int[] ret = new int[k];
        for (int i = 0; i < k; i++) {
            int val = maxHeap.poll();
            ret[i] = val;
        }
        return ret;
    }
    public static void main(String[] args) {
        int[] array = {4,1,9,2,8,0,7,3,6,5};
        int[] ret = smallestK(array,3);
        System.out.println(Arrays.toString(ret));
    }
}

image.gif

目录
相关文章
|
3天前
|
存储 设计模式 算法
JAVA中的常见数据结构
JAVA中的常见数据结构
|
1月前
|
存储 缓存 监控
Java面试题:在Java中,对象何时可以被垃圾回收?编程中,如何更好地做好垃圾回收处理?
Java面试题:在Java中,对象何时可以被垃圾回收?编程中,如何更好地做好垃圾回收处理?
34 0
|
5天前
|
缓存 前端开发 Java
【前端学java】复习巩固-Java中的对象比较(15)
【8月更文挑战第11天】Java中的对象比较
15 1
【前端学java】复习巩固-Java中的对象比较(15)
|
11天前
|
存储 Java 程序员
Java中对象几种类型的内存分配(JVM对象储存机制)
Java中对象几种类型的内存分配(JVM对象储存机制)
45 5
Java中对象几种类型的内存分配(JVM对象储存机制)
|
3天前
|
Java API 开发者
|
5天前
|
存储 Java
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
这篇文章通过Java代码示例展示了如何实现哈希表,包括定义结点类、链表类、数组存储多条链表,并使用简单的散列函数处理冲突,以及如何利用哈希表存储和查询学生信息。
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
|
6天前
|
存储 Java
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
这篇文章通过Java代码示例展示了如何实现哈希表,包括定义结点类、链表类、数组存储多条链表,并使用简单的散列函数处理冲突,以及如何利用哈希表存储和查询学生信息。
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
|
11天前
|
存储 Java 程序员
08 Java面向对象基础(对象与类+实例变量与方法+构造方法+this关键字)
08 Java面向对象基础(对象与类+实例变量与方法+构造方法+this关键字)
33 4
|
15天前
|
存储 安全 Java
揭秘Java序列化神器Serializable:一键解锁对象穿越时空的超能力,你的数据旅行不再受限,震撼登场!
【8月更文挑战第4天】Serializable是Java中的魔术钥匙,开启对象穿越时空的能力。作为序列化的核心,它让复杂对象的复制与传输变得简单。通过实现此接口,对象能被序列化成字节流,实现本地存储或网络传输,再通过反序列化恢复原状。尽管使用方便,但序列化过程耗时且存在安全风险,需谨慎使用。
27 7
|
2天前
|
存储 设计模式 Java
在 Java 中创建多个对象
【8月更文挑战第17天】
6 0