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

news2024/12/23 7:07:13

文章目录

  • 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;

    @Override
    public 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>() {
            @Override
            public 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() 返回枚举常量,不存在的会报错 IllegalArgumentException
        System.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.coloradmin.cn/o/957892.html

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

相关文章

【双指针】

1、输入一个字符串&#xff0c;将字符串中的单词按照空格分隔开来&#xff08;分别打印出来&#xff09;。 假设这个字符串的开头是没有空格的&#xff0c;并且每两个单词中间只有一个空格。 #include<bits/stdc.h> using namespace std;int main(){char str[111];// ge…

界面控件DevExpress WinForms(v23.2)下半年发展路线图

本文主要概述了官方在下半年&#xff08;v23.2&#xff09;中一些与DevExpress WinForms相关的开发计划&#xff0c;重点关注的领域将是可访问性支持和支持.NET 8。 DevExpress WinForms有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。同时能…

C语言常见关键字:一文打尽

关键字 1. 前言2. 什么是关键字3. extern-声明外部符号4. auto-自动5. typedef-类型重定义&#xff08;类型重命名&#xff09;6. register-寄存器6.1 存储器6.2 register关键字的作用 7. static-静态7.1 static修饰局部变量7.1.1 代码对比7.1.2 原理分析 7.2 static修饰全局变…

iOS import包

Frameworks Frameworks 顾名思义就是框架&#xff0c;是第三方打包完成看不到源码&#xff0c;可以直接使用的 在项目中引用方式 OC 引用某一个文件&#xff0c;Frameworks一般会提供一个h文件引用全部其他文件 #import <JLRoutes/JLRoutes.h>swift 引用一个包&#x…

如何使用Puppeteer进行金融数据抓取和预测

导语 Puppeteer是一个基于Node.js的库&#xff0c;可以用来控制Chrome或Chromium浏览器&#xff0c;实现网页操作、截图、PDF生成等功能。本文将介绍如何使用Puppeteer进行金融数据抓取和预测&#xff0c;以及如何使用亿牛云爬虫代理提高爬虫效果。 概述 金融数据抓取是指从…

RunnerGo:轻量级、全栈式、易用性和高效性的测试工具

随着软件测试的重要性日益凸显&#xff0c;市场上的测试工具也日益丰富。RunnerGo作为一款基于Go语言研发的开源测试平台&#xff0c;以其轻量级、全栈式、易用性和高效性的特点&#xff0c;在测试工具市场中逐渐脱颖而出。 RunnerGo是一款轻量级的测试工具&#xff0c;使用Go…

C# PaddleDetection yolo 印章检测

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Extensions; using Sdcb.PaddleDetection; using Sdcb.PaddleInference; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq…

九、MySQL(DQL基础查询)如何查询表中信息?

1、DQL基础用法&#xff1a; 2、实例&#xff1a; &#xff08;1&#xff09;初始化表格&#xff1a; # 创建表头 create table things(id int comment 编号,number int comment 学号,name char(5) comment 姓名,address char(6) comment 地址,phone number int comment 电话…

传输层—UDP原理详解

目录 前言 1.netstat 2.pidof 3.UDP协议格式 4.UDP的特点 5.面向数据报 6.UDP的缓冲区 7.UDP使用注意事项 8.基于UDP的应用层协议 总结 前言 在之前的文章中为大家介绍了关于网络协议栈第一层就是应用层&#xff0c;包含套接字的使用&#xff0c;在应用层编码实现服务…

go学习part21(3)redis连接池

连接池 1.介绍 每次使用数据就就建立链接再关闭可以&#xff0c;但是如果有大量客户端频繁请求连接&#xff0c;大量创建连接和关闭会非常耗费资源。 所以就建立一个连接池&#xff0c;里面存放几个不关闭的连接&#xff0c;谁要用就分配给谁。 说明:通过Golang 对 Redis操…

hadoop学习:mapreduce的wordcount时候,继承mapper没有对应的mapreduce的包

踩坑描述&#xff1a;在学习 hadoop 的时候使用hadoop 下的 mapreduce&#xff0c;却发现没有 mapreduce。 第一反应就是去看看 maven 的路径对不对 settings——》搜索框搜索 maven 检查一下 Maven 路径对不对 OK 这里是对的 那么是不是依赖下载失败导致 mapreduce 没下下…

vscode和python离线安装超详细

1、下载vscode和python 1、vscode win7的用第一个版本,win10两个都可以,我测试的是第二个版本 如果不想去找的,文末可以从我这下载 2、python 如果已有python环境包,可以不用,我就是直接用的python环境包 如果不想去找的,文末可以从我这下载 3、python插件(pytho…

MATLAB中isequal函数转化为C语言

背景 有项目算法使用matlab中isequal函数进行运算&#xff0c;这里需要将转化为C语言&#xff0c;从而模拟算法运行&#xff0c;将算法移植到qt。 MATLAB中isequal简单介绍 语法 tf isequal(A,B) tf isequal(A1,A2,...,An) 说明 如果 A 和 B 等效&#xff0c;则 tf is…

《人工智能算法图解》书籍分享(包邮送书)

文章目录 人工智能介绍书籍分享抽奖包邮送书 人工智能介绍 人工智能算法是一种能够模拟人类智能行为的计算机算法。它通过分析和处理大量的数据&#xff0c;利用机器学习、深度学习和自然语言处理等技术&#xff0c;实现自主学习、推理和决策的能力。 人工智能算法的发展经历…

基于位置管理的企业员工考勤打卡系统设计 微信小程序

员工考勤打卡系统设计app是针对员工必不可少的一个部分。在公司发展的整个过程中&#xff0c;员工考勤打卡系统设计app担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类员工考勤打卡系统设计app程序也在不断改进。本课题所设计的 MVC基于HBuilder X的员工考勤…

【GO】LGTM_Grafana_Tempo(1)_架构

最近在尝试用 LGTM 来实现 Go 微服务的可观测性&#xff0c;就顺便整理一下文档。 Tempo 会分为 4 篇文章&#xff1a; Tempo 的架构官网测试实操跑通gin 框架发送 trace 数据到 tempogo-zero 微服务框架使用发送数据到 tempo 第一篇是关于&#xff0c;tempo 的架构&#xff…

图像库 PIL(一)

Python 提供了 PIL&#xff08;python image library&#xff09;图像库&#xff0c;来满足开发者处理图像的功能&#xff0c;该库提供了广泛的文件格式支持&#xff0c;包括常见的 JPEG、PNG、GIF 等&#xff0c;它提供了图像创建、图像显示、图像处理等功能。 基本概念 要学…

app易用性测试报告 软件app测试

易用性测试 app易用性测试应遵从GB/T25000.10-2016、GB/T25000.51-2016中的有关成熟性、可用性、容错性、易恢复性等方面的可靠性要求。依据应用场景需要&#xff0c;可让用户较长时间连续运行或使用APP&#xff0c;不应出现崩溃、闪退、卡死、无响应、响应迟缓等问题。 根据…

浅谈城市轨道交通视频监控与AI视频智能分析解决方案

一、背景分析 地铁作为重要的公共场所交通枢纽&#xff0c;流动性非常高、人员大量聚集&#xff0c;轨道交通需要利用视频监控系统来实现全程、全方位的安全防范&#xff0c;这也是保证地铁行车组织和安全的重要手段。调度员和车站值班员通过系统监管列车运行、客流情况、变电…

Java设计模式:四、行为型模式-04:中介者模式

文章目录 一、定义&#xff1a;中介者模式二、模拟场景&#xff1a;中介者模式三、违背方案&#xff1a;中介者模式3.1 工程结构3.2 创建数据库3.3 JDBC工具类3.4 单元测试 四、改善代码&#xff1a;中介者模式4.1 工程结构4.2 中介者工程结构图4.3 资源和配置类4.3.1 XML配置对…