JAVASE进阶:Collection高级(1)——源码分析contains方法、lambda遍历集合

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:JAVASE进阶:函数式编程——lambda表达式替代匿名内部类
📚订阅专栏:JAVASE进阶
希望文章对你们有所帮助

打算法竞赛的时候用的C++,为了方便敲代码基本上都不怎么用iterator来遍历集合,都是直接使用的下标。
但在JAVA,下标遍历的方式不适合所有的集合,因为集合中还包含了Set,其没有索引的概念。又因为Java具有泛型编程,所以通用的iterator遍历是很重要的,除此之外还有其他两种遍历方式,当然其底层肯定还是用的迭代器。
其实使用起来都是很容易的,学了就会,但是还是要扒一扒源码才有意思。

源码分析contains方法、函数式编程实现遍历

  • Collection体系结构
    • contains()底层原理
  • 遍历Collection的三种通用方式
    • 迭代器方式
    • 增强for方式
    • lambda表达式方式

Collection体系结构

在这里插入图片描述
其中vector已经被淘汰了。

可以看到,Collection除了线性的List,还有不存在索引概念的Set。他们的特点各不相同:

List集合的特点:元素有序、可重复、有索引
Set集合的特点:元素无序、不重复、无索引

因为Java推荐使用泛型编程,等式左边声明的对象一般都是泛型的Collection,所以需要学会通用的遍历方式。

Collection常用通用方法:add()、clear()、remove()、contains()、isEmpty()、size()。除了contains(),其他方法的底层都是很容易的,但是contains()使用不慎就会出错。

contains()底层原理

在了解底层原理的时候,可以先看下面的语句:

Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
boolean result = coll.contains("aaa");
System.out.println(result);

这里集合中包含"aaa",因此执行结果是true

再看下面语句,将学生对象加入容器再执行contains方法:

Collection<Student> coll = new ArrayList<>();
Student s1 = new Student("zhangsan", 20);
Student s2 = new Student("lisi", 21);
Student s3 = new Student("wangwu", 22);
coll.add(s1);
coll.add(s2);
coll.add(s3);
Student s4 = new Student("wangwu", 22);
boolean result = coll.contains(s4);
System.out.println(result);

这里最终返回的结果是false

这是为什么?这需要查看contains方法的源码了:

1、跟踪contains方法,可以看到这是一个通用的抽象方法:
在这里插入图片描述
2、返回,选中contains并右键点击Go To再进入Implemention,找到ArrayList的具体实现类:
在这里插入图片描述
3、contains传入了泛型对象o,然后将o再放入indexOf函数,indexOf将o、开始下标0、结束下标size放入indexOfRange函数并返回:
在这里插入图片描述
4、跟踪indexOfRange函数,可以发现,底层会遍历集合,并使用equals函数判断对象和集合中的对象是否相同:
在这里插入图片描述
5、跟踪进入equals方法,可以发现其比较对象是否相等的方式是==
在这里插入图片描述

所以,第一个案例中,全部都是字符串常量,来源于常量池,比较的时候两个字符串对象指向了常量池中的同一个字符串,因此比较会成功。
而第二个案例中是自定义对象,其创建的过程是在堆之中的,且每次new出来的都是不一样的,引用对象的==比较的是地址值,地址值显然是不一样的,因此会返回false。

可以得出结论:contains方法底层使用equals方法判断对象是否在容器中,因此默认只能使用字符串才能正常判断

想要解决,需要在自定义的Javabean中重写equals方法,将其重写为判断元素是否完全相等即可。

遍历Collection的三种通用方式

迭代器方式

方法名称说明
boolean hasNext()判断当前位置是否有元素,有元素返回true,没有返回false
E next()获取当前位置的元素,并将迭代器对象移向下一个位置
Iterator<String> it = list.iterator();
boolean flag = it.hasNext();
String str = it.next();
System.out.println(str);

需要注意一些细节:

1、遍历完了再访问会报错NoSuchElementException
2、迭代器遍历完毕,指针不会复位
3、循环中只能使用一次next方法
4、迭代器遍历时,不能用集合的方法进行增加或者删除

增强for方式

相关知识:

1、增强for的底层就是迭代器,它是为了简化迭代器的代码书写的。
2、它是JDK5之后出现的,其内部原理就是一个Iterator迭代器。
3、所有的单列集合和数组才能用增强for进行遍历

格式:

for(元素数据类型:变量名 数组或集合){

}

lambda表达式方式

lambda表达式实现遍历提供了一种更简单直接的遍历集合的方式,其遍历其实是使用了forEach函数:

方法名称说明
default void forEach(Consumer<? super T> action)结合lambda遍历集合

可以稍微扒一扒forEach的底层源码:
1、找到forEach方法,其主要的意思就是遍历集合,依次得到容器中的每一个元素,并传递给action.accept方法,剩下的处理就交给accept了:
在这里插入图片描述
2、跟踪进入accept方法,可以看到其所在的Consumer接口上的@FunctionalInterface注解,那就说明可以使用函数式编程了:
在这里插入图片描述
若不考虑函数式编程,forEach的使用可以采用匿名内部类,即new出一个匿名内部类对象,并重写其中的accept方法,表示接收并编写处理的方式(在这里处理方式为打印):

	public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }

若使用函数式编程,代码可以简化为:

	coll.forEach((String o) -> {
        System.out.println(o);
    });

又根据函数式编程的简化规则(若不了解看上一篇文章),可以将代码进一步简化为:

	coll.forEach(o -> System.out.println(o));

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024 高级前端面试题之 HTTP模块 「精选篇」

该内容主要整理关于 HTTP模块 的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 HTTP模块精选篇 1. HTTP 报文的组成部分2. 常见状态码3. 从输入URL到呈现页面过程3.1 简洁3.2 详细 4. TCP、UDP相关5. HTTP2相关6. https相关7. WebSocket的…

docker-compose Install HertzBeat

HertzBeat前言 HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,兼容 Prometheus,无需 Agent 的开源实时监控告警系统。 易用友好的开源实时监控告警系统,无需Agent,高性能集群,兼容Prometheus,强大自定义监控能力。​ 集 监控+告警+通知 为一体,支持对…

ToF传感器在移动机器人中的作用

原创 | 文 BFT机器人 在日新月异的机器人技术领域&#xff0c;技术的无缝整合正引领着人类与机器交互方式的革新潮流。ToF传感器作为变革性创新的一个例子&#xff0c;对移动机器人更好地感知周围环境起到了决定性的作用。 ToF传感器与激光雷达技术在创建深度图方面有着异曲同…

SpringBoot实战2

目录 1.如何返回两个类型的数据&#xff1f;User和Booth 2.如何使用MyBatis遍历一个数组进行查询&#xff1f; 3.前端要的数据太多太杂&#xff0c;我们拼接多个List&#xff0c;前端找数据困难&#xff0c;浪费时间。因此我们进行三表联表查询。 1.首先创建一个vo包&#x…

c++ STL less 的视角

c less 函数在不同的地方感觉所起的作用是不一样的&#xff0c; 这中间原因是 less 的视角不一样&#xff0c; 下面尝试给出解释下&#xff0c; 方便记忆 1、 左右视角 符合 排序sort less(value, element&#xff09; less 表示一种 “符合关系“&#xff0c; 表示sort 后…

大数据环境搭建(一)-Hive

1 hive介绍 由Facebook开源的,用于解决海量结构化日志的数据统计的项目 本质上是将HQL转化为MapReduce、Tez、Spark等程序 Hive表的数据是HDFS上的目录和文件 Hive元数据 metastore&#xff0c;包含Hive表的数据库、表名、列、分区、表类型、表所在目录等。 根据Hive部署模…

蓝桥杯第九届省赛题-----彩灯控制系统笔记

题目要求&#xff1a; 一、 基本要求 1.1 使用 CT107D 单片机竞赛板&#xff0c;完成“彩灯控制器”功能的程序设计与调 试&#xff1b; 1.2 设计与调试过程中&#xff0c;可参考组委会提供的“资源数据包”&#xff1b; 1.3 Keil 工程文件以准考证号命名&#xff0c…

在VM虚拟机搭建NFS服务器

NFS共享要求如下&#xff1a; &#xff08;1&#xff09;共享“/mnt/自已姓名的完整汉语拼音”目录&#xff0c;允许XXX网段的计算机访问该共享目录&#xff0c;可进行读写操作。&#xff08;说明&#xff1a;XXX网段&#xff0c;请根据你的规划&#xff0c;再具体指定&#xf…

VUE开发记录

1、VUE模板传递参数到JS方法 <select-language :value"item.language" change"selectLanguage($event, key)"></select-language>selectLanguage(value, key){console.log(value, key) }, 2、Element框架el-form-item自定义label和内容 <…

远秋医学培训系统未授权查看密码

指纹特征 title"远秋医学培训报名系统v1.0"漏洞复现 POC&#xff1a;/User/ManagerList.aspx?ty1&ty1

好的问卷设计标准:确保数据质量与准确性的关键要素

问卷的主要由三个部分组成&#xff1a;问卷说明、问卷主题、问卷结束。而这三个部分又包含了很多因素&#xff0c;比如问卷主题、问卷标题、问卷题目、问卷调查对象等。制作问卷不仅仅是简单的问题罗列&#xff0c;然后进行发放。不同质量的调查问卷会反馈出不一样的效果&#…

高级变量赋值和变量的间接引用

1.高级变量赋值 var${str-lucky} 变量配置方式 var${str:-lucky} 变量配置方式 var${strlucky} 变量配置方式 2.变量的间接引用 eval 命令 eval命令将会首先扫描命令行进行所有的置换&#xff0c;然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量,该命令对变…

MySQL学习记录——일 MySQL 安装、配置

文章目录 1、卸载内置环境2、安装MySQL3、启动4、登录5、配置my.cnf 当前环境是1核2G云服务器&#xff0c;CentOS7.6。要在root用户下进行操作 1、卸载内置环境 云服务器中有可能会自带mysql还有mariadb这样的数据库服务&#xff0c;在安装我们mysql前&#xff0c;得先查找一下…

Java学习六、数组的定义与使用

一、数组的创建及初始化 数组是相同类型元素的集合&#xff0c;在内存中是一段连续的空间。 1.数组的创建 T[] 数组名 new T[N]; T&#xff1a;表示数组中存放元素的类型 N:表示数组长度 int[] array1 new int[10];//创建一个可以容纳10个int类型元素的数组 double[] array…

BeanDefinitionRegistry学习

Spring版本5.1.x 简介 在Spring框架中&#xff0c;BeanDefinitionRegistry是一个接口&#xff0c;它主要用于向注册表中注册BeanDefinition实例&#xff0c;完成注册的过程。该接口的主要方法是registerBeanDefinition&#xff0c;用于将一个BeanDefinition实例注册到注册表中…

2023年09月CCF-GESP编程能力等级认证Python编程一级真题解析

Python等级认证GESP(1~6级)全部真题・点这里 一、单选题(共15题,共30分) 第1题 我们通常说的“内存”属于计算机中的 ( )。 A:输出设备 B:输入设备 C:存储设备 D:打印设备 答案:C 第2题 以下Python不可以作为变量的名称的是 ( )。 A:redStar B:RedStar C:red…

在RunnerGo测试平台中做WebSocket、Dubbo、TCP/IP接口测试

大家好&#xff0c;RunnerGo作为一款一站式测试平台不断为用户提供更好的使用体验&#xff0c;最近得知RunnerGo新增对&#xff0c;WebSocket、Dubbo、TCP/IP&#xff0c;三种协议API的测试支持&#xff0c;本篇文章跟大家分享一下使用方法。 WebSocket协议 WebSocket 是一种…

前端面试拼图-数据结构与算法

摘要&#xff1a;总结一些前端算法题&#xff0c;持续更新&#xff01; 一、数据结构与算法 时间复杂度-程序执行时需要的计算量&#xff08;CPU&#xff09; 空间复杂度-程序执行时需要的内存空间 前端开发&#xff1a;重时间&#xff0c;轻空间 1.把一个数组旋转k步 arr…

UnityShader(十四)纹理

目录 前言&#xff1a; 单张纹理实现效果&#xff1a; 效果&#xff1a; 前言&#xff1a; 纹理最初的目的是用一张图片来控制模型的外观。使用纹理映射技术我们可以把一张图“贴”在模型表面&#xff0c;逐纹素&#xff08;文素的名字是为了和像素进行区分&#xff09;控制…

开源软件:引领技术创新、商业模式与安全的融合

序 在信息技术日新月异的今天&#xff0c;开源软件以其独特的魅力和影响力&#xff0c;正逐渐成为软件产业的新常态。开源软件的低成本、高度可协作性和透明度等特点&#xff0c;不仅吸引了无数企业和个人用户的青睐&#xff0c;更为软件行业带来了前所未有的繁荣景象。 一、…