java比较器、迭代器和枚举类型详解

文章目录

  • 1. 比较器
    • 1.1 Comparable
      • 实例:对自定义对象进行排序
    • 1.2 Comparator
      • 实例:对自定义对象进行排序
    • 1.3 equals
      • 1.3.1 equals介绍
      • 1.3.2 == 详解
  • 2. 迭代器
    • 2.1 Iterator
    • 2.2 ListIterator
  • 3. 枚举
    • 3.1 枚举访问
    • 3.1 枚举细节

1. 比较器

比较器指的是集合存储的元素的特性,如果元素是可比较的则可以进行相应的排序,否则不行。

Java中常用的比较器有 Comparable 接口和 comparator 接口 ,下面我们来介绍一下这两个比较器。

1.1 Comparable

Comparable 接口被称为自然排序,也叫做内部比较器,对于 Comparable 接口来说,它往往是进行比较类需要实现的接口,它仅包含一个有 compareTo() 方法,只有一个参数,返回值为 int

  • 其返回值大于 0 0 0 表示对象大于参数对象;
  • 其返回值小于 0 0 0表示对象小于参数对象;
  • 其返回值等于 0 0 0表示两者相等 。

之所以称之为自然排序,那是因为像 String, Integer 等类中都实现了Comparable 接口的 compareTo() 方法,所以如果对一个包含String, Integer 等基础类的列表进行排序的话,其可以按照数字大小或者字母顺序进行排序,就是因为它们都实现了该方法。比如 String 类中的 compareTo 方法实现如下:

    public int compareTo(String anotherString) {int len1 = value.length;int len2 = anotherString.value.length;int lim = Math.min(len1, len2);char v1[] = value;char v2[] = anotherString.value;int k = 0;while (k < lim) {char c1 = v1[k];char c2 = v2[k];if (c1 != c2) {return c1 - c2;}k++;}return len1 - len2;}

从上面可以看出,它称为内部比较器还是有原因的,首先,它存在于类的内部,其次,它只传入了一个数据与本类的数据进行内部的比较。

接下来我们使用 Comparable 比较器实现一个按照零食价格进行排序的例子。

实例:对自定义对象进行排序

我们首先定义一个零食对象,并将其实现 Comparable 接口中的 compareTo 方法,按照价格进行排序,代码如下:

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
class snack implements Comparable{private String name;private double price;@Overridepublic int compareTo(Object o) {//按照图书价格从小到大排序if (o instanceof snack){snack s = (snack) o;// 调用Double类中重写的compare方法,也可以自己写,依据返回值比较大小return Double.compare(this.price, s.price);}throw new RuntimeException("传入的数据类型不一致!");}
}

然后创建几个对象并对其使用数组的sort方法进行排序,之后再输出各个元素进行查看。

public class Solution {public static void main(String[] args) {snack[] snacks = new snack[]{new snack("奥利奥", 6.5), new snack("巧乐兹", 3),new snack("趣多多", 7), new snack("呀土豆", 5)};Arrays.sort(snacks);for (snack snack : snacks) {System.out.println(snack);}}
}

输出结果如下:

snack(name=巧乐兹, price=3.0)
snack(name=呀土豆, price=5.0)
snack(name=奥利奥, price=6.5)
snack(name=趣多多, price=7.0)

1.2 Comparator

Comparator 被称作外部比较器,当元素的类型没实现 java.lang.Comparable 接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序。

对于Comparator接口来说,它的实现者被称为比较器,它包含一个compare()方法,有两个参数,返回值与ComparablecompareTo()方法一样,不同之处是Comparator接口一般不会被集合元素类所实现,而是单独实现或者匿名内部类方式实现。

其实现的方法签名为 compare(Object o1,Object o2) ,也就是比较 o1,o2 的大小,

  • 如果方法返回正整数,则表示o1大于o2
  • 如果返回 0 0 0,表示相等;
  • 返回负整数,表示o1小于o2

实例:对自定义对象进行排序

由于我们使用外部比较器实现对象的比较,这里对象就不需要实现接口了,故对象的定义如下:

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
class snack{private String name;private double price;
}

然后我们在sort方法中写入匿名内部类的比较器,定义我们的比较规则,代码如下:

public class Solution {public static void main(String[] args) {snack[] snacks = new snack[]{new snack("奥利奥", 6.5), new snack("巧乐兹", 3),new snack("趣多多", 7), new snack("呀土豆", 5)};// 使用匿名内部类定义外部比较器Arrays.sort(snacks, new Comparator<snack>() {@Overridepublic int compare(snack o1, snack o2) {return Double.compare(o1.getPrice(), o2.getPrice());}});for (snack snack : snacks) {System.out.println(snack);}}
}

1.3 equals

1.3.1 equals介绍

前面两个比较器的实现都可以用来进行排序,这里 equals 的实现只是用来判断是否相等的

在Java中,使用 == 进行比较,比较的是两个引用对象的地址是否相等,很多时候这种比较并不能达到我们需要的目的,所以,这就出现了 equals 方法。

equals 方法是Object类的默认方法,即每个对象都能够调用该方法,当该对象没有重写此方法时,调用此方法使用的就是Object中的代码,Object中该方法的实现如下:

public boolean equals(Object obj) {return (this == obj);
}

可以看到,Object中的该方法与 == 起的效果是一样的,而String等类中调用 equals 方法能够比较值的不同,那是因为 String 等类重写了该方法,同理,如果自定义的类中也需要比较不同值,那么可以考虑重写 equals 方法。

1.3.2 == 详解

在使用基本数据类型 boolean,byte,char,short,int,long,float,double 进行比较时,如果使用 == ,比价的是其值的大小。

那么,为什么下面的代码输出是 true 呢?

public class Solution {public static void main(String[] args) {// 自动装箱Integer a =  3;Integer b = 3;System.out.println(a == b); //输出true}
}

首先,我们来了解以下自动装箱和自动拆箱的相关知识。

自动装箱就是Java编译器在需要的时候,自动将原始数据类型转换为对应的包装类类型,例如将int 类型的值装箱成 Integer类型的对象。

自动拆箱就是Java编译器在需要的时候,自动将包装类型对象转换为对应的基础数据类型,例如将Integer类型的对象装箱成 int类型的值。举例的话如下:

Integer a = 10;  // 自动装箱
int b = a;  // 自动拆箱

了解了自动装箱后,应该明白了上述代码的 Integer a = 3;Integer b = 3; 已经属于两个对象了,为什么使用 == 会相等呢?

那是因为内部人员已经将 [-128, 127] 之间的整数装箱完毕,当程序中使用该范围之间的整数时,无需装箱直接取用自动装箱池中的对象即可,这也就表示 Integer a = 3;Integer b = 3; 其实装箱的都是已经装箱完毕的同一个对象,所以使用 == 时返回的是 true

完整的流程如下:

自动装箱时首先判断i值是否在[-128,127]这个区间中,如果在这个区间中,则直接从IntegerCache.cache缓存中获取指定数字的包装类,IntegerCache内部实现了一个Integer的静态常量数组,在类加载的时候,执行static静态块进行初始化[-128,127]之间的Integer对象, 存放到cache数组中,cache数组属于常量,存放在java的方法区中,这样调用时会使用缓存池中的对象,多次调用会取得同一个对象的引用,不在这个区间中则new出一个新的包装类。

于是,像下述代码,直接new 的一个对象,并未使用缓存中的对象,判断是肯定不相等的,

public class Solution {public static void main(String[] args) {Integer a =  new Integer(3);Integer b = 3;System.out.println(a == b); // 输出false}
}

2. 迭代器

迭代器是一种对象,它允许按顺序访问集合中的元素,而不需要知道集合的底层结构。通过使用迭代器,我们可以遍历集合并访问其中的元素,而无需关心集合的具体实现方式。

Java 提供了 Iterator 接口作为迭代器的基础接口。该接口定义了一组用于访问集合元素的方法,包括 hasNextnextremove 等,该接口下还有 ListIterator Spliterator 等子接口,接下来我们对 Iterator, ListIterator 进行介绍。

2.1 Iterator

Iterator 接口是所有迭代器的祖先,它是 Java 操作集合元素的标准方式之一。它提供了对集合元素进行遍历和删除的基本方法,Collection 中所有的集合都实现了 Iterator 接口,该接口定义了几个方法,其中最常用的方法如下:

函数签名功能
boolean hasNext()如果迭代器可以迭代下一个元素,返回 true
E next()返回迭代器的下一个元素
void remove()移除当前迭代器的元素

当使用 Iterator 进行迭代的时候,最开始其位于第一个元素的前面,当使用一次 next() 函数后,其到达第一个元素后、第二个元素前,其并不直接指向元素,如下图所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当我们需要进行迭代的时候,可以按照如下方式进行迭代:

public class Solution {public static void main(String[] args) {ArrayList<String> arrayList = new ArrayList<>();arrayList.add("java");arrayList.add("python");arrayList.add("C");arrayList.add("C#");arrayList.add("javascript");// 使用迭代器Iterator<String> iterator = arrayList.iterator();while(iterator.hasNext()){String item = iterator.next();System.out.println(item);}}
}

当然,我们也可以使用增强 for 循环来进行循环迭代,它也是使用的迭代器来进行遍历,而且简化了迭代器的使用,不需要显式地使用迭代器,如下:

public class Solution {public static void main(String[] args) {ArrayList<String> arrayList = new ArrayList<>();arrayList.add("java");arrayList.add("python");arrayList.add("C");arrayList.add("C#");arrayList.add("javascript");// 增强for进行遍历for (String item : arrayList) {System.out.println(item);}}
}

2.2 ListIterator

ListIterator 接口继承自 Iterator 接口,它专门用于遍历 List 集合的元素,Iterator 接口只能从前往后遍历集合元素,而 ListIterator 接口提供了从前往后以及从后往前遍历集合元素的方法,还可以在遍历的过程中添加、修改、删除元素。

ListIterator 接口的主要方法如下:

函数签名功能
boolean hasNext()如果迭代器可以迭代下一个元素,返回 true
E next()返回迭代器的下一个元素
void remove()移除当前迭代器的元素
boolean hasPrevious()如果迭代器可以迭代前一个元素,返回 true
E previous()返回迭代器的前一个元素

可以使用上面的示例为:

public class Solution {public static void main(String[] args) {ArrayList<String> arrayList = new ArrayList<>();arrayList.add("java");arrayList.add("python");arrayList.add("C");arrayList.add("C#");arrayList.add("javascript");System.out.println("正序:");ListIterator<String> listIterator = arrayList.listIterator();while(listIterator.hasNext()) {String item = listIterator.next();System.out.println(item);}System.out.println("逆序:");while(listIterator.hasPrevious()) {String item = listIterator.previous();System.out.println(item);}}
}

3. 枚举

Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一年的 12 个月份,一个星期的 7 天,方向有东南西北等。

Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。

一个简单的枚举定义如下:

enum Direction{WEST, EAST, SOUTH, NORTH
}

即使用 enum 关键字即可定义。

3.1 枚举访问

枚举常用的方法如下:

函数签名功能
values()返回枚举类中所有的值
ordinal()找到每个枚举常量的索引
valueOf()返回指定字符串值的枚举常量

上述方法的使用如下:

public class Solution {public static void main(String[] args) {// 调用 values()Direction[] arr = Direction.values();// 迭代枚举for (Direction direction : arr) {// 查看索引System.out.println(direction + " at index " + direction.ordinal());}// 使用 valueOf() 返回枚举常量,不存在的会报错 IllegalArgumentExceptionSystem.out.println(Direction.valueOf("WEST"));}
}

代码执行的结果如下:

WEST at index 0
EAST at index 1
SOUTH at index 2
NORTH at index 3
WEST

定义了上述枚举类机器变量后,如果需要对其进行单独的访问,除了使用 valueOf 函数,还可以直接访问其成员,代码如下:

public class Solution {public static void main(String[] args) {Direction d1 = Direction.EAST;System.out.println(d1);}
}

输出结果为:

EAST

3.1 枚举细节

看过了上面的代码后,不知道你们会不会问,为什么枚举的变量可以使用类似成员变量的访问形式 Direction.EAST 来进行访问呢?

其实,每个枚举都是通过 Class 在内部实现的,且所有的枚举值都是 public static final 的。上面的枚举类 Direction 转化在内部类中实现为:

class Direction{public static final Direction WEST = new Direction();public static final Direction EAST = new Direction();public static final Direction NORTH = new Direction();public static final Direction SOUTH = new Direction();
}

不仅如此,枚举类也可以在类的内部进行使用,比如:

public class Test { enum Direction { WEST, EAST, SOUTH, NORTH} // 执行输出结果public static void main(String[] args) { Direction d1 = Direction.WEST; System.out.println(d1); } 
}

了解了枚举类中的每个变量都是通过无参构造方法而来的后,我们可以对每个枚举的变量添加参数,比如:

enum Direction{WEST("西方"),EAST("东方"),SOUTH("南方"),NORTH("北方");private String ChineseName;private Direction(String name){this.ChineseName = name;}public String getChineseName() {return ChineseName;}
}

了解到枚举的变量本质后,上述代码的 WEST("西方") 一行其实就是 public static final Direction WEST = new Direction("西方"); ,相当于就是初始化一个有参构造方法,所以我们增加了一个变量 ChineseName ,用来记录我们传入的中文名字,如果想要对这个变量进行访问,同样,按照函数的形式进行访问即可。

public class Solution {public static void main(String[] args) {Direction d1 = Direction.EAST;String name = d1.getChineseName();System.out.println(name); //输出 "东方"}
}
}

}


了解到枚举的变量本质后,上述代码的 `WEST("西方")` 一行其实就是 `public static final Direction WEST = new Direction("西方");`  ,相当于就是初始化一个有参构造方法,所以我们增加了一个变量 `ChineseName` ,用来记录我们传入的中文名字,如果想要对这个变量进行访问,同样,按照函数的形式进行访问即可。```java
public class Solution {public static void main(String[] args) {Direction d1 = Direction.EAST;String name = d1.getChineseName();System.out.println(name); //输出 "东方"}
}

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

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

相关文章

【玩玩Vue】使用el-menu作为菜单时,通过一二级路由控制菜单高亮

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 文章目录 前言一、Vue路由二、路由一级控制高亮&#xff08;常用&#xff09;1.vue中路由文件2.网址样式3.Vue文件4.$route的内容 三、路由二级控制高亮…

element中Notification组件(this.$notify)自定义样式

1、自定义样式效果 2、vue代码 this.notifications this.$notify({title: ,dangerouslyUseHTMLString: true,duration: obj.remindMethod3 ? 0:4500,customClass: notify-warning,offset: 50,showClose: false,message: this.$createElement("div",null,[this.$…

手把手教你写出第一个C语言程序

Hello, World! 1. 前言2. 准备知识2.1 环境2.2 文件的分类2.3 注释2.3.1 注释的作用2.3.2 注释的两种风格2.3.2.1 C语言的注释风格2.3.2.2 C的注释风格 2.3.3 VS中注释和取消注释的快捷键 3. 开始演示3.1 创建项目3.2 创建源文件3.3 写代码3.4 编译链接运行 4. 代码解释4.1 写主…

com.google.guava:guava 组件安全漏洞及健康分析

组件简介 维护者google组织许可证类型Apache-2.0首次发布2010 年 4 月 26 日最新发布时间2023 年 8 月 1 日GitHub Star48189GitHub Fork10716依赖包28,694依赖存储库219,576 Guava 是 Google 的一组核心 Java 库&#xff0c;其中包括新的集合类型&#xff08;例如 multimap 和…

前端:html实现页面切换、顶部标签栏(可删、可切换,点击左侧超链接出现标签栏)

一、在一个页面&#xff08;不跨页面&#xff09; 效果&#xff1a; 代码 <!DOCTYPE html> <html><head><style>/* 设置标签页外层容器样式 */.tab-container {width: 100%;background-color: #f1f1f1;overflow: hidden;}/* 设置标签页选项卡的样式 …

【C++】C++11新特性 lambda表达式

C11新特性 lambda表达式1、引入2、lambda表达式语法3、 捕获列表说明4、 lambda表达式的原理5、 lambda对象的大小 lambda表达式 1、引入 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法&#xff0c;如果待排序元素为自定义类…

springboot自定义表格(动态合并单元格)

一、需求展示&#xff08;一个订单多个商品&#xff0c;商品数量不限订单行合并&#xff09; 二、技术选型&#xff08;jxls自定义模板&#xff09; <!-- 版本具体看官网Release&#xff0c;这里我们使用 2.13.0 --><dependency><groupId>org.jxls</group…

fastdds之core

目录 Entity Entity

基于JavaWeb和mysql实现校园订餐前后台管理系统(源码+数据库)

一、项目简介 本项目是一套基于JavaWeb和mysql实现网上书城前后端管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都…

Kubernetes技术--k8s核心技术 configMap

1.概述 configMap最主要的作用是存储一些不加密的数据到/etcd,让pod以变量或者数据卷(volume)挂载到容器。 应用场景:配置文件、存储信息等 2.使用 -1.创建配置文件。 这里我们需要先编写一个配置文件。使用redis,如下所示:

Android studio实现圆形进度条

参考博客 效果图 MainActivity import androidx.appcompat.app.AppCompatActivity; import android.graphics.Color; import android.os.Bundle; import android.widget.TextView;import java.util.Timer; import java.util.TimerTask;public class MainActivity extends App…

2023开学季图书馆荐八一新书《乡村振兴战略下传统村落文化旅游设计 》中大许少辉博士后著

2023开学季图书馆荐八一新书《乡村振兴战略下传统村落文化旅游设计 》中大许少辉博士后著

说说大表关联小表

分析&回答 Hive 大表和小表的关联 优先选择将小表放在内存中。小表不足以放到内存中&#xff0c;可以通过bucket-map-join(不清楚的话看底部文章)来实现&#xff0c;效果很明显。 两个表join的时候&#xff0c;其方法是两个join表在join key上都做hash bucket&#xff0c…

【数据结构回顾】

数据结构回顾 一、单链表二、单循环链表 一、单链表 #include <stdio.h> #include <stdlib.h>typedef struct Node {int data;Node *next; }Node;Node* initList() {Node *list (Node*)malloc(sizeof(Node));list->data 0;list->next NULL;return list; }…

Netty源码NioEventLoop解析

带着问题源码 Netty 的 NioEventLoop 是如何实现的&#xff1f;它为什么能够保证 Channel 的操作是线程安全的&#xff1f;Netty 如何解决 JDK epoll 空轮询 Bug&#xff1f;NioEventLoop 是如何实现无锁化的&#xff1f; 一、作用与设计原理 Netty的NioEventLoop并不是一个存…

pg_database中的datlastsysoid

一&#xff0c;关于 pg_database 在 PostgreSQL 中&#xff0c;对于在数据库集群内创建的每个数据库,其关键信息都会被保存到 pg_database 系统表中。 PostgreSQL 确保通过 pg_database 系统表持久化存储每个数据库的属性信息&#xff0c;以方便后续管理和使用。这也让 pg_da…

Windows安装配置Tomcat服务器教程 - 外网远程访问

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器&#xff0c;不仅名字很有趣&#xff0…

振动智能监测与设备可靠性:无线技术的契机

在现代工业领域&#xff0c;设备的可靠性和稳定运行对于生产效率和安全性至关重要。然而&#xff0c;由于设备的频繁使用和各种环境影响&#xff0c;设备故障和突发停机仍然是不可避免的挑战。为了有效地应对这些挑战&#xff0c;振动智能监测技术结合无线传感器的应用正在成为…

W5500-EVB-PICO主动PING主机IP检测连通性(十)

前言 上一章我们用W5500_EVB_PICO 开发板做UDP组播数据回环测试&#xff0c;那么本章我们进行W5500_EVB_PICO Ping的测试。 什么是PING&#xff1f; Ping &#xff08;Packet Internet Groper&#xff09;是一种因特网包探索器&#xff0c;用于测试网络连接量的程序 。Ping是…

“北科Java面试宝典(211最详细讲解)“

Version : V1.0 北科Java面试宝典一、Java基础面试题【24道】二、JVM虚拟机面试题【14道】三、集合相关面试题【17道】四、多线程 【25道】五、IO【5道】六、网络编程 【9道】七、MySQL以及SQL面试题【20道】八、常用框架【19道】九、中间件和分布式 【54道】十、设计模式面试 …