初学数据结构:Java对象的比较
目录
- 1. PriorityQueue中插入对象
- 2. 元素的比较
- 2.1 基本类型的比较
- 2.2 对象比较的问题
- 3. 对象的比较
- 3.1 基于Comparable接口类的比较
- 3.2 基于比较器比较
- 3.3 三种方式对比
- 4. 集合框架中PriorityQueue的比较方式
- 5. 使用PriorityQueue创建大小堆,解决TOPK问题
【本节目标】
- Java中对象的比较
- 集合框架中PriorityQueue的比较方式
- 模拟实现PriorityQueue
1. 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(){PriorityQueue<Card> p = new PriorityQueue<>();p.offer(new Card(1, "♠"));p.offer(new Card(2, "♠"));}public static void main(String[] args) {TestPriorityQueue();}
}
优先级队列底层使用堆,而向堆中插入元素时,为了满足堆的性质,必须要进行元素的比较,而此时Card是没有办法直接进行比较的,因此抛出异常
2. 元素的比较
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);}
}
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指向的是同一个对象}
}
从编译结果可以看出,Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较。 那为什么==可以比较?
因为:对于用户实现自定义类型,都默认继承自Object类,而Object类中提供了equal方法,而==默认情况下调用的就是equal方法,但是该方法的比较规则是:没有比较引用变量引用对象的内容,而是直接比较引用变量的地址,但有些情况下该种比较就不符合题意。
// Object中equal的实现,可以看到:直接比较的是两个引用变量的地址
public boolean equals(Object obj) {return (this == obj);
}
3. 对象的比较
有些情况下,需要比较的是对象中的内容,比如:向优先级队列中插入某个对象时,需要对按照对象中内容来调整堆,那该如何处理呢
public class Card {public int rank; // 数值public String suit; // 花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;} @Overridepublic 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 this.rank == c.rank && suit.equals(c.suit);}
}
注意: 一般覆写 equals 的套路就是上面演示的
- 如果指向同一个对象,返回 true
- 如果传入的为 null,返回 false
- 如果传入的对象类型不是 Card,返回 false
- 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌
- 注意调用其他引用类型的比较也需要 equals,例如这里的 suit 的比较
覆写基类equal的方式虽然可以比较,但缺陷是:equal只能按照相等进行比较,不能按照大于、小于的方式进行比较。
3.1 基于Comparable接口类的比较
Comparable是JDK提供的泛型的比较接口类,源码实现具体如下
public interface Comparable<E> {// 返回值:// < 0: 表示 this 指向的对象小于 o 指向的对象// == 0: 表示 this 指向的对象等于 o 指向的对象// > 0: 表示 this 指向的对象大于 o 指向的对象int compareTo(E o);
}
对用用户自定义类型,如果要想按照大小与方式进行比较时:在定义类时,实现Comparable接口即可,然后在类中重写compareTo方法
public class Card implements Comparable<Card> {public int rank; // 数值public String suit; // 花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;} // 根据数值比较,不管花色// 这里我们认为 null 是最小的@Overridepublic int compareTo(Card o) {if (o == null) {return 1;} return rank - o.rank;}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 比较大}
}
Comparable是java.lang中的接口类,可以直接使用
3.2 基于比较器比较
按照比较器方式进行比较,具体步骤如下
-
用户自定义比较器类,实现Comparator接口
public interface Comparator<T> {// 返回值:// < 0: 表示 o1 指向的对象小于 o2 指向的对象// == 0: 表示 o1 指向的对象等于 o2 指向的对象// > 0: 表示 o1 指向的对象等于 o2 指向的对象int compare(T o1, T o2); }注意:区分Comparable和Comparator
-
覆写Comparator中的compare方法
import java.util.Comparator; class Card {public int rank; // 数值public String suit; // 花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;} }class CardComparator implements Comparator<Card> {// 根据数值比较,不管花色// 这里我们认为 null 是最小的@Overridepublic 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 比较大} }注意:Comparator是java.util 包中的泛型接口类,使用时必须导入对应的包
3.3 三种方式对比
| 覆写的方法 | 说明 |
|---|---|
| Object.equals | 因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与否 |
| Comparable.compareTo | 需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序 |
| Comparator.compare | 需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性强 |
4. 集合框架中PriorityQueue的比较方式
集合框架中的PriorityQueue底层使用堆结构,因此其内部的元素必须要能够比大小,PriorityQueue采用了: Comparable和Comparator两种方式
- Comparable是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparable接口,并覆写compareTo方法
- 用户也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口并覆写compare方法
// 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置为nullpublic 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 compatibilityif (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);elsesiftUpComparable(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;}
}
5. 使用PriorityQueue创建大小堆,解决TOPK问题
//使用比较器创建小根堆
class LessIntComp implements Comparator<Integer>{@Overridepublic int compare(Integer o1, Integer o2) {return o1 - o2;}
}
//使用比较器创建大根堆
class GreaterIntComp implements Comparator<Integer>{@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}
}
public class TestDemo<E> {//求最小的K个数,通过比较器创建大根堆public static int[] smallestK(int[] array, int k) {if(k <= 0) {return null;} 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));}
}
相关文章:
初学数据结构:Java对象的比较
目录 1. PriorityQueue中插入对象2. 元素的比较2.1 基本类型的比较2.2 对象比较的问题 3. 对象的比较3.1 基于Comparable接口类的比较3.2 基于比较器比较3.3 三种方式对比 4. 集合框架中PriorityQueue的比较方式5. 使用PriorityQueue创建大小堆,解决TOPK问题 【本节…...
mac 10.15.7 Unity 2021.3.14 XCode 12.4 -> Unity IOS 自动安装 Cocoapods 失败解决方法
自己这两天在用Unity开发IOS时,遇到了安装Cocoapods失败的问题,记录一下问题及解决方法,便于自己后续查看,以及有相同遭遇的人查看 发生场景:打开 unity,触发自动安装 Cocoapods -> 安装失败(…...
Elasticsearch 中使用MustNot等同于不登录遇到的坑
1、在写关键词推荐时,需要把当前文章过滤掉,不能再推荐自己的文章,所以再es中需要用到 MustNot属性查询 /// <summary> /// 服务中心es检索 /// </summary> /// <param name="input"></param> /// <returns></…...
java抽象工厂实战与总结
文章目录 一、工厂模式(三种)1.简单工厂模式1.1 概念:1.2 使用场景:1.3 模型图解:1.4 伪代码: 2.工厂方法模式2.1 概念:2.2 使用场景:2.3 模型图解:2.4 伪代码 3.抽象工厂…...
Compose | UI组件(六) | 选择框
文章目录 前言Checkbox 复选框的含义Checkbox 复选框的使用Switch 单选框的含义Switch 单选框的使用Slider 滑竿组件的含义Slider 滑竿组件的使用 总结 前言 随着移动端的技术不断更新迭代,Compose也运用的越来越广泛,很多人都开始学习Compose 本文主要…...
C++拷贝构造函数、赋值学习整理:
拷贝构造函数: 概念: 构造函数的第一个参数,是类本身的const引用(一般情况下没有其他参数,少数情况:其他参数必须有默认值!)称此类构造函数为拷贝构造函数 特征: 1&am…...
[亲测源码]ps软件网页版在线使用 PS网站程序源码 photoshop网页版源码 网页版的ps软件源码
在线PS作图修图网页版PHP网站源码,PHP在线照片图片处理PS网站程序源码photoshop网页版。 有很多朋友们都是在用PS作图的,众所周知在使用和学习PS时是需要下载软件的,Photoshop软件对电脑配置也是有一定要求的,今天就为大家带来一…...
前端大厂面试题探索编辑部——第二期
目录 题目 单选题1 题解 关于TCP 关于UDP 单选题2 题解 A选项的HTTP是否是无状态协议 B选项的HTTP支持的方法 C选项的关于HTTP的状态码 D选项HTTP协议的传输格式 题目 单选题1 1.以下哪个描述是关于 TCP 和 UDP 的区别() A. TCP 是无连接的…...
yaml学习笔记
文章目录 yaml语言学习yaml 简介yaml 和json 区别基本语法数据类型YAML 对象YAML 数组锚点和引用纯量 参考文档 yaml语言学习 最近发现在学习k8s中各种配置文件 都是使用的yaml 这种格式, 包括 docker-compose.yaml 也都是用这个格式配置部署项目信息,我就了解了一下这个语法就…...
深度强化学习(王树森)笔记04
深度强化学习(DRL) 本文是学习笔记,如有侵权,请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接:https://github.com/wangshusen/DRL 源代码链接:https://github.c…...
openssl3.2/test/certs - 074 - CT entry
文章目录 openssl3.2/test/certs - 074 - CT entry概述笔记setup074.shsetup074_sc1.shsetup074_sc2.shsetup074_sc3.shEND openssl3.2/test/certs - 074 - CT entry 概述 openssl3.2 - 官方demo学习 - test - certs 笔记 setup074.sh #! /bin/bash# \file setup074.sh# o…...
Angular组件(一) 分割面板ShrinkSplitter
Angular组件(一) 分割面板ShrinkSplitter 前言 分割面板在日常开发中经常使用,可将一片区域,分割为可以拖拽整宽度或高度的两部分区域。模仿iview的分割面板组件,用angular实现该功能,支持拖拽和[(ngModel)]双向绑定的方式控制区…...
抖音详情API:视频内容获取与解析技巧
一、引言 抖音是一款广受欢迎的短视频分享平台,每天都有大量的用户在抖音上分享自己的生活点滴和创意作品。对于开发者而言,如何获取并解析抖音上的视频内容,是一项极具挑战性的任务。本文将详细介绍抖音详情API,以及如何使用它来…...
SpringBoot中实现阿里云OSS对象存储
背景 在业务中我们往往需要上传文件如图片,文件上传,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发抖音、发朋友圈都用到了文件上传功能。 实现文件…...
大型语言模型 (LLM)全解读
一、大型语言模型(Large Language Model)定义 大型语言模型 是一种深度学习算法,可以执行各种自然语言处理 (NLP) 任务。 大型语言模型底层使用多个转换器模型, 底层转换器是一组神经网络。 大型语言模型是使用海量数据集进行训练…...
Unity - gamma space下还原linear space效果
文章目录 环境目的环境问题实践结果处理要点处理细节【OnPostProcessTexture 实现 sRGB 2 Linear 编码】 - 预处理【封装个简单的 *.cginc】 - shader runtime【shader需要gamma space下还原记得 #define _RECOVERY_LINEAR_IN_GAMMA】【颜色参数应用前 和 颜色贴图采样后】【灯…...
Rabbitmq调用FeignClient接口失败
文章目录 一、框架及逻辑介绍1.背景服务介绍2.问题逻辑介绍 二、代码1.A服务2.B服务3.C服务 三、解决思路1.确认B调用C服务接口是否能正常调通2.确认B服务是否能正常调用A服务3.确认消息能否正常消费4.总结 四、修改代码验证1.B服务异步调用C服务接口——失败2.将消费消息放到C…...
专业120+总分400+海南大学838信号与系统考研高分经验海大电子信息与通信
今年专业838信号与系统120,总分400,顺利上岸海南大学,这一年的复习起起伏伏,但是最后还是坚持下来的,吃过的苦都是值得,总结一下自己的复习经历,希望对大家复习有帮助。首先我想先强调一下专业课…...
如何区分 html 和 html5?
HTML(超文本标记语言)和HTML5在很多方面都存在显著的区别。HTML5是HTML的最新版本,引入了许多新的特性和元素,以支持更丰富的网页内容和更复杂的交互。以下是一些区分HTML和HTML5的关键点: 新特性与元素:H…...
Ps:将文件载入堆栈
Ps菜单:文件/脚本/将文件载入堆栈 Scripts/Load Files into Stack 将文件载入堆栈 Load Files into Stack脚本命令可用于将两个及以上的文件载入到同一个 Photoshop 新文档中。 载入的每个文件都将成为独立的图层,并使用其原始文件名作为图层名。 Photos…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
用递归算法解锁「子集」问题 —— LeetCode 78题解析
文章目录 一、题目介绍二、递归思路详解:从决策树开始理解三、解法一:二叉决策树 DFS四、解法二:组合式回溯写法(推荐)五、解法对比 递归算法是编程中一种非常强大且常见的思想,它能够优雅地解决很多复杂的…...
