《Java初阶数据结构》----8.<java对象的比较总结>

news2024/9/19 9:10:45

目录

前言

一、Java对象的比较

1.1基本类型的比较

 1.2 对象比较的问题(==与equals)

1.3对象的比较 (三种常用方式)

1.重写equals方法

2.基于Comparble接口类的比较

3.基于比较器比较(Comparator接口)

三种方式对比

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

2.1 PriorityQueue中插入对象

2.2 PriorityQueue采用的Comparble和Comparator两种方式。

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


前言

      大家好,我目前在学习java。之前也学了一段时间,但是没有发布博客。时间过的真的很快。我会利用好这个暑假,来复习之前学过的内容,并整理好之前写过的博客进行发布。如果博客中有错误或者没有读懂的地方。热烈欢迎大家在评论区进行讨论!!!

      喜欢我文章的兄弟姐妹们可以点赞,收藏和评论我的文章。喜欢我的兄弟姐妹们以及也想复习一遍java知识的兄弟姐妹们可以关注我呦,我会持续更新滴,
     望支持!!!!! 一起加油呀!!!!

语言只是工具,不能决定你好不好找工作,决定你好不好找工作的是你的能力!!!

学历本科及以上就够用了!!!


本篇博客主要讲解Java基础语法中的、

1.java中对象的比较

基本类型的比较、对象比较的问题(==与equals)、对象的比较 (三种常用方式)

2.集合框架中PriorityQueue的比较方式采用的Comparble和Comparator两种方式。

3.使用PriorityQueue创建大小堆,解决TOPK问题

一、Java对象的比较

1.1基本类型的比较

我们知道基本类型的数据可以直接比较大小

比较整型

        int a = 10;
        int b = 20;
        System.out.println(a > b);
        System.out.println(a < b);
        System.out.println(a == b);

运行结果

false
true
false

 比较字符型

        char c1 = 'A';
        char c2 = 'B';
        System.out.println(c1 > c2);
        System.out.println(c1 < c2);
        System.out.println(c1 == c2);

运行结果

false
true
false

比较布尔型 

        boolean b1 = true;
        boolean b2 = false;
        System.out.println(b1 == b2);
        System.out.println(b1 != b2);

运行结果

false
true

 1.2 对象比较的问题(==与equals)

我们定义一张卡牌,第一张是红桃1的牌。第二张是红桃2牌。

第三张与第一张一样是红桃1。

class Card{
    public int rank;//数值
    public String suit;//花色

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
}
public class Main {
    public static void main(String[] args) {
        Card card1 = new Card(1,"♥");
        Card card2 = new Card(2,"♥");
        Card card3 =card1;
        System.out.println(card1 == card2);
        System.out.println(card1 == card3);
    }
}
//运行结果
false
true

card1和card2指向不同的对象。因此打印false。(哈希值不同)

card1和card3是指向同一个对象因此打印true(哈希值相同)

注意:我们不能使用大于号、小于号去比较。

因为Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较。会编译报错。

可以使用==去比较,是因为用户实现自定义类型默认继承自Object类,而Object类中提供了equal方法,而==默认情况下调用的就是equals方法。

但是该方法的比较规则是:没有比较引用变量引用对象的内容,而是直接比较引用变量的地址(也就是哈希值)

  Object中equals的实现源码。

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

1.3对象的比较 (三种常用方式)

1.重写equals方法

有些情况下,需要比较的是对象中的内容,比如:向优先级队列中插入某个对象时,需要对按照对象中内容来调整堆,那该如何处理呢?

我们需要重写父类的equals方法。

如下代码中,我们在Card类中重写父类Object的equals方法。

this的含义。this是本类Card的类型。而谁调用了这个方法,this就是谁的引用。

我们通过

return this.rank == c.rank && this.suit.equals(c.suit);

这个来比较对象里面的内容。

注意基本类型可以直接比较,但引用类型最好调用其equal方法。如比较字符串suit我们最好使用equals。而不是==。

因为 == 用于检查两个字符串变量是否引用同一个对象。String类的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 obj) {
        if (this == obj){
            return true;
        }
        //如果obj为null或者obj不是Card的子类。返回false
        if ((obj == null || !(obj instanceof Card))){
            return false;
        }
        Card c = (Card) obj;
        //this的含义。this本类Card的类型。而谁调用了这个方法,this就是谁的引用。
        return this.rank == c.rank && this.suit.equals(c.suit);
        //我们通过return这个来比较对象里面的内容。
    }
}
public class Main {
    public static void main(String[] args) {
        Card card1 = new Card(1,"♥");
        Card card2 = new Card(1,"♥");
        Card card3 = new Card(2,"♥");
        Card card4 =card1;
        System.out.println(card1 == card2);
        System.out.println(card1.equals(card2));
        System.out.println(card1.equals(card3));
        System.out.println(card1.equals(card4));
    }
}
//运行结果
false
true
false

 运行结果:

1.card1和card2。使用==比较。对象里面的内容虽然相同但是,引用地址不同。因此第一个是false。

2.card1和card2。使用我们重写的equals比较。对象里面的内容相同,因此返回true。

3.card1和card3。使用我们重写的equals比较。对象内容不同因此返回false

注:

一般重写 equals 就是上面演示的

1. 如果指向同一个对象,返回 true

2. 如果传入的为 null,返回 false

3. 如果传入的对象类型不是 Card,返回 false

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

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

2.基于Comparble接口类的比较

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

public interface Comparable<T> {
    public int compareTo(T o);
}

返回值:

< 0: 表示 this 指向的对象小于 o 指向的对象

== 0: 表示 this 指向的对象等于 o 指向的对象

> 0: 表示 this 指向的对象大于 o 指向的对象

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

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 int compareTo(Card o) {
        if (o == null){
            return 1;//>0.表示 this 指向的对象大于 o 指向的对象
        }

        return this.rank - o.rank;
        //>0.表示 this 指向的对象大于 o 指向的对象
        //=0.表示 this 指向的对象等于 o 指向的对象
        //<0.表示 this 指向的对象小于 o 指向的对象
    }
}
public class Main {
    public static void main(String[] args) {
        Card card1 = new Card(2,"♥");
        Card card2 = new Card(1,"♥");
        Card card3 = new Card(3,"♥");
        Card card4 = new Card(3,"♥");
        System.out.println(card1.compareTo(card2));
        System.out.println(card1.compareTo(card3));
        System.out.println(card3.compareTo(card4));
    }
}
//运行结果:
1 //表示card1更大
-1 //表示card2更大
0 //表示card3和card4一样大

注意:

1.如下代码,是泛型比较,别忘了<Card>。

class Card implements Comparable<Card>

 Compareble是java.lang中的接口类,可以直接使用。不需要导入包。

3.基于比较器比较(Comparator接口)

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

1.用户自定义比较器类,实现Comparator接口。Comparator接口比较的源码如下:

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

2.与Comparble接口的比较方法的返回值类似。

3.重写Comparator中的compare方法

    @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;
    }

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

完整代码如下:

class Card{
    public int rank;//数值
    public String suit;//花色

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

}
//定义一个比较器类,专门实现Card的比较
class CardComparator implements Comparator<Card> {
    @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 class Main {
    public static void main(String[] args) {
        Card card1 = new Card(2,"♥");
        Card card2 = new Card(1,"♥");
        Card card3 = new Card(3,"♥");
        Card card4 = new Card(3,"♥");

        //定义比较器对象
        CardComparator comparator = new CardComparator();

        System.out.println(comparator.compare(card1,card2));
        System.out.println(comparator.compare(card1,card3));
        System.out.println(comparator.compare(card3,card4));
    }
}
//运行结果
1//表示card1 > card2
-1//表示card1 < card3
0//表示card3 = card4

三种方式对比

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

2.1 PriorityQueue中插入对象

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

优先级队列底层使用堆,而向堆中插入元素时,为了满足堆的性质,必须要进行元素的比较。而如果Card没有实现比较的方法,则会抛出异常。

2.2 PriorityQueue采用的Comparble和Comparator两种方式。

集合框架中的PriorityQueue底层使用堆结构,因此其内部的元素必须要能够比大小 

PriorityQueue采用了: Comparble和Comparator两种方式。

 1. Comparble是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparble接口,并覆写compareTo方法

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

JDK中PriorityQueue的实现:

// JDK中PriorityQueue的实现:
public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable {
    // ...
    
    // 默认容量
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    
    // 内部定义的比较器对象,用来接收用户实例化PriorityQueue对象时提供的比较器对象
    private final Comparator<? super E> comparator;
    
    // 用户如果没有提供比较器对象,使用默认的内部比较,将comparator置为null
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
   }
    
    // 如果用户提供了比较器,采用用户提供的比较器进行比较
    public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
   }
    
    // ...
    // 向上调整:
    // 如果用户没有提供比较器对象,采用Comparable进行比较
    // 否则使用用户提供的比较器对象进行比较
    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
   }
 
   // 使用Comparable
    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
       }
        queue[k] = key;
   }
 
    // 使用用户提供的比较器对象进行比较
    @SuppressWarnings("unchecked")
    private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }
}

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

使用比较器创建小根堆

//使用比较器创建小根堆
class LessIntComp implements Comparator<Integer>{
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2;
    }
}

使用比较器创建大根堆 

//使用比较器创建大根堆
class GreaterIntComp implements Comparator<Integer>{
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
}

 解决TOPK问题

public class TestDemo<E> {
        //求最小的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));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1951924.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在网站嵌入可填写的PDF表单:2024巴黎奥运会赛程

如何将可填写的 PDF 表单嵌入您的网页&#xff1f;访问者无需下载或注册即可查看并填写。 简单&#xff01;本文以2024巴黎奥运会赛程表单为例&#xff0c;演示如何将其嵌入网页中。您可以在 ONLYOFFICE 表单库免费获取该模板&#xff0c;有白色和紫色两种背景设计。 如何在网站…

uniapp中出现图片过小会与盒子偏离

结论&#xff1a;在image的父盒子中加上display: flex&#xff0c;原因不清楚 出问题的代码和图片如下&#xff1a; <template><view style" background-color: greenyellow; height: 10rpx;width: 10rpx;"><image :src"imgSrc.seatnull" …

UCOS-III 任务调度锁定/解锁接口OSSchedLock/Unlock详解

在实时操作系统中&#xff0c;任务调度是系统实现多任务并发执行的核心机制。但在某些场景下&#xff0c;我们需要临时关闭任务调度&#xff0c;以确保某段代码在执行过程中不被打断。下面将详细介绍这两个接口的应用场景及实现原理。 1. 关闭调度的应用场景 关闭任务调度的主要…

Windows环境下部署本地大模型教程

自ChatGPT在2022年爆发式出圈以来&#xff0c;大模型对人类世界产生了重大影响&#xff0c;无论是在生产、工作、写作方面还是自动驾驶领域&#xff0c;大模型发挥了非常重要的作用。 现各大厂&#xff0c;无论是国内大厂还是国外大厂&#xff0c;都纷纷推出了自己的对话大模型…

服务暴露 traefik

一。traefik 部署 前置资源 还是那个网站&#xff0c;这里复制 entryPoints&#xff0c;进入traefik的大门 可选在哪台机器上部署 traefik 部署 用 Daemonset 的方式是为了&#xff0c;加机器到集群后&#xff0c;能自动部署traefik 到目标机器 注意 8084端口 必须加…

【JVM基础06】——组成-直接内存详解

目录 1- 引言&#xff1a;直接内存概述1-1 直接内存是什么&#xff1f;直接内存的定义(What)1-2 为什么用直接内存&#xff1f;Java程序对直接内存的使用 (Why) 2- ⭐核心&#xff1a;详解直接内存(How)2-1 文件拷贝案例介绍对比常规 IO(BIO) 和 NIO常规 IO 的操作流程NIO 的操…

C++ - char*、const char*、char[]、string

const char* const char* 用来定义字符串常量。 char[ ] char型的字符数组是一种定长的数组&#xff0c;存储指定长度的字符序列&#xff0c;数组中的每个元素都是一个char类型的变量&#xff0c;如&#xff1a; char arr[] {h, a, l, l, o, \0}; char c arr[0]; // 访问…

Spring Boot的Web开发

目录 Spring Boot的Web开发 1.静态资源映射规则 第一种静态资源映射规则 2.enjoy模板引擎 3.springMVC 3.1请求处理 RequestMapping DeleteMapping 删除 PutMapping 修改 GetMapping 查询 PostMapping 新增 3.2参数绑定 一.支持数据类型: 3.3常用注解 一.Request…

[Vulnhub] Raven2 PHPMailer-RCE+MSQP:Mysql权限提升

信息收集 IP AddressOpening Ports192.168.101.160TCP:22,80,111,46606 $ nmap -p- 192.168.101.160 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 6.7p1 Debian 5deb8u4 (protocol 2.0) | ssh-hostkey: | 1024 26:81:c1:f…

拉提查合创5步玩转git工具协作代码开发

1 工具使用场景 开发团队使用git版本管理工具&#xff0c;进行协作代码开发过程中&#xff0c;最常用的场景为&#xff1a; &#xff08;1&#xff09;拉取代码 将git远端仓库最新代码拉取到本地。 &#xff08;2&#xff09;提交代码 将本地新增修改的代码提交至git远端仓库中…

Spring Cloud微服务项目统一封装数据响应体

在微服务架构下&#xff0c;处理服务之间的通信和数据一致性是一个重要的挑战。为了提高开发效率、保证数据的一致性及简化前端开发&#xff0c;统一封装数据响应体是一种非常有效的实践。本文博主将介绍如何在 Spring Cloud 微服务项目中统一封装数据响应体&#xff0c;并分享…

【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现

1 绪论 1.1 研究背景与意义 在气候变化日益受到全球关注的背景下&#xff0c;天气气温的变化已经对人们的生活各方面都产生了影响&#xff0c;人们在外出时大多都会在手机上看看天气如何&#xff0c;根据天气的变化来决定衣物的穿着和出行的安排。[1]如今手机能提供的信息已经…

C++基础知识:函数重载相关注意事项:1.引用作为重载条件,2.2.函数重载遇见函数默认参数。

1.引用作为重载条件 #include<iostream>using namespace std;//1.引用作为重载的条件 //int 和 const int 类型不同&#xff0c;所以可以作用重载条件 void fn(int &a) //int &a10;不合法 //10放在了常量区&#xff0c;而引用要么在栈区&#xff0c;要么在堆区{…

mysql的MHA以及故障模拟

目录 MHA概念 MHA的组件 MHA的特点 实验&#xff1a;搭建完成MHA的架构 实验&#xff1a;主备切换 实验结果 实验&#xff1a;故障切换 实验&#xff1a;故障恢复 MHA概念 MHA&#xff1a;高可用模式下的故障切换&#xff0c;基于主从复制。它解决的是单点故障和主从复…

Ruby、Python、Java 开发者必备:Codigger之软件项目体检

在编程的广阔天地里&#xff0c;Ruby、Python 和 Java 开发者们各自凭借着独特的语言特性&#xff0c;构建着精彩纷呈的应用世界。然而&#xff0c;无论使用哪种语言&#xff0c;确保项目的高质量始终是至关重要的目标。而 Codigger 项目体检则成为了实现这一目标的得力助手&am…

【JS】事件循环

事件循环&#xff08;Event Loop&#xff09; 什么是事件循环 事件循环就是一个执行任务队列的机制 为什么会出现事件循环 为了避免单线程出现阻塞,采用了异步的形式执行任务。为了解决异步任务的问题,采用了事件循环机制。 事件循环的流程 JavaScript是一门单线程的语言…

Chapter 17 Python文件操作(下)

欢迎大家订阅【Python从入门到精通】专栏&#xff0c;一起探索Python的无限可能&#xff01; 文章目录 前言一、文件的写入二、文件的追加 前言 Python作为一种高效且易于学习的编程语言&#xff0c;提供了一系列强大的文件操作功能&#xff0c;使得用户能够轻松地实现文件的读…

深度解析Linux-C——函数和内存管理

目录 函数指针&#xff1a; 指针函数&#xff1a; 参数为指针的函数&#xff1a; 参数为数组的函数&#xff1a; C语言内存管理 stdlib.h头文件常用函数介绍 1、局部变量 2、全局变量 3、 堆空间变量 4、静态变量 5、常量 函数指针&#xff1a; 指向函数的指针&#…

针对datax-web 中Swagger UI接口未授权访问

application.yml 添加以下配置 实现访问doc.html 以及/v2/api-docs 接口时需要进行简单的校验 swagger:basic:enable: trueusername: adminpassword: 12345 配置重启后再进行相关访问则需要输入用户名和密码

Radon(拉当) 变换:超详细讲解(附MATLAB,Python 代码)

Radon 变换 Radon 变换是数学上用于函数或图像的一种积分变换&#xff0c;广泛应用于图像处理领域&#xff0c;尤其是在计算机断层成像 (CT) 中。本文档将详细介绍 Radon 变换的数学含义及其在图像处理中的应用。 数学定义 Radon 变换的数学定义是将二维函数 f ( x , y ) f…