Java集合——List接口学习总结
一、ArrayList实现类
1. 常用方法
增加:add(int index, E element)删除:remove(int index) remove(Object o)修改:set(int index, E element)查看:get(int index)判断:常用遍历方式:
//List集合 遍历://方式1:普通for循环:System.out.println("---------------------");for(int i = 0;i<list.size();i++){System.out.println(list.get(i));}//方式2:增强for循环:System.out.println("---------------------");for(Object obj:list){System.out.println(obj);}//方式3:迭代器:System.out.println("---------------------");Iterator it = list.iterator();while(it.hasNext()){System.out.println(it.next());}
2. JDK1.8(jdk1.8.0_331)源码下(简要)
重要成员变量和构造方法
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{//重要成员变量private static final int DEFAULT_CAPACITY = 10; //数组默认初始容量private static final Object[] EMPTY_ELEMENTDATA = {}; //实例对象的默认数组,是个空数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //与EMPTY_ELEMENTDATA类似,主要用于区分在首次添加元素时判断如何进行扩容transient Object[] elementData; //ArrayList中实际存储元素的Object数组private int size; //计算ArrayList数组的元素数量,不是elementData.lengthprivate static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //要分配的数组的最大大小,也就是数组极限长度protected transient int modCount = 0;//给迭代器用的,在调用add和remove方法时修改数值//构造方法//空参构造,例如List list = new ArrayList<>();public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //初始化数组为空数组}//根据传入的initialCapacity,判断初始化数组长度为多少,例如List list1 = new ArrayList<>(100);public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity]; // >0时初始化} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA; //等于0时初始化为空数组} else {throw new IllegalArgumentException("Illegal Capacity: "+ //小于0时,抛出异常,代码健壮性考虑initialCapacity);}}//传入集合Collection下面的实现类,? extends E这个表达式涉及到泛型知识/*List中 <? extends xxx> 定义了List集合的泛型上限,就是定义了一个天花板,输入的类只能从天花板往下找当前类或子类List中<? super xxx> 定义了泛型的下限,就是定义了一个地板,输入的类只能从地板开始往上找当前类或者父类例如:List<Object> a = new ArrayList<>();List<Person> b = new ArrayList<>();List<Student> c = new ArrayList<>();List<? extends Person> list1 = null;List<? super Person> list2 = null;list1 = a;//编译报错,因为list1定义的泛型最大父类(天花板)就是Person,Object类是所有类的父类,超过了定义的天花板Person类list1 = b;//编译正常list1 = c;//编译正常list2 = a;//编译正常list2 = b;//编译正常list2 = c;//编译报错,因为list2定义的泛型最小父类(地板)就是Person,Student类是Person类子类,是低于定义的地板Person类List<? extends Person>:就相当于:List<? extends Person>是List<Person>的父类,也是List<Person的子类>的父类List<? super Person>是List<Person>的父类,也是List<Person的父类>的父类*/public ArrayList(Collection<? extends E> c) {Object[] a = c.toArray(); //转成Obj数组,赋值给aif ((size = a.length) != 0) { //判断数组长度是否为0if (c.getClass() == ArrayList.class) { //判断c的类名是不是ArrayList//是的话就直接赋值给数组elementDataelementData = a; } else {//否则就拷贝复制新数组给elementDataelementData = Arrays.copyOf(a, size, Object[].class); }} else {//数组长度不为0,则实例初始化为空数组elementData = EMPTY_ELEMENTDATA;}}
}
常用方法:add
//常用方法addpublic boolean add(E e) {ensureCapacityInternal(size + 1);//这一步是计算和判断数组容量elementData[size++] = e; //将传入元素放在数组下标为size的位置,然后size+1,下标是从0开始return true;}private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果数组是个空数组的话,返回DEFAULT_CAPACITY和minCapacity的最大值return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}private void ensureExplicitCapacity(int minCapacity) {modCount++; //迭代器用的计数值,可忽略if (minCapacity - elementData.length > 0) //当 size+1 也就是当前数组元素数量+1,大于当前elementData数组长度时,就需要针对elementData数组扩容grow(minCapacity);}private void grow(int minCapacity) {int oldCapacity = elementData.length; //原先老数组长度int newCapacity = oldCapacity + (oldCapacity >> 1); //新数组长度计算,大概是1.5倍,旧数组长度+(旧数组长度/2)if (newCapacity - minCapacity < 0)newCapacity = minCapacity; //newCapacity < minCapacity时,newCapacity = minCapacityif (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity); //newCapacity超出数组定义的极限长度时的处理elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) { //超出极限长度时if (minCapacity < 0) // overflowthrow new OutOfMemoryError(); //抛出异常//判断是否大于MAX_ARRAY_SIZE,返回是话Integer.MAX_VALUE,否则MAX_ARRAY_SIZE。//MAX_ARRAY_SIZE之所以是Integer.MAX_VALUE - 8,多出来的8 int(整数) 就等于32 bytes(字节)是防止内存溢出用的,更深入的话涉及JVM和计算机底层了。return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
样例理解:插入第一个元素时

插入超过10个元素时:

常用方法:remove,这个可以自己看源码,实际原理就是先找到元素e,然后通过System.arraycopy方法把元素e后面所有元素往前移动一位,再把元素末尾置位null
public E remove(int index) {rangeCheck(index); //下标检查modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;}public boolean remove(Object o) {if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;}private void fastRemove(int index) {modCount++;int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its work}
总结:
- ArrayList底层是使用Object数组实现的,当创建对象时,若使用无参构造,则初始容量为0,此时第一次调用add方法添加就需要扩容为10,元素数量超过数组长度时会进行数组扩容,如需再次扩容为1.5倍。
- 优点:底层是数组,查询效率高,数据可重复
- 缺点:添加或者删除时效率低
3.在JDK1.7和1.8中的区别

4.ListIterator迭代器

出错原因:就是迭代器it和list同时对集合进行操作导致。
出错详解:调用add方法时,ArrayList成员变量里操作次数modCount发生了改变,例如上述代码调用到的add(“ee”)时ArrayList中的成员变量modCount=5(执行了5次add)。list.iterator()返回的Iterator iterator实际是ArrayList源码中内部类Itr实现了接口Iterator。在ArrayList源码中内部类Itr有个成员变量expectedModCount(预期的操作次数)记录,在调用完add(“kk”)后modCount=6,但是expectedModCount还是5,然后继续while走到if时调用next()方法时发现两个变量不一致抛出了并发修改异常。
源码图解:

解决方案:事情让一个“人”做 --》引入新的迭代器:ListIterator。迭代和添加操作都是靠ListIterator来完成的,ListIterator底层实际就通过在同一个数组上,用指针的概念在调整位置。

实际上看源码就知道做了什么事情,就是ArrayList源码实现接口ListIterator自己实现了add方法,把expectedModCount和modCount同步一下。同时内部类Itr实现了remove方法,跟内部类ListItr的add方法是一个道理。


5.面试题:iterator(),Iterator,Iterable关系

1.iterator()是Iterable接口中定义的方法,用于返回一个迭代器(Iterator)对象。
2.Iterator接口是Java集合框架中用于遍历集合元素的标准接口,它定义了一些方法,如hasNext()、next()、remove()等,用于遍历集合中的元素并对其进行操作。
3.Iterable接口是Java集合框架中用于表示“可迭代”的对象的标准接口,它定义了一个iterator()方法,用于返回一个迭代器(Iterator)对象,从而可以遍历集合中的元素。
hashNext()、next()方法源码简化

二、Vector实现类
1. 与ArrayList区别与联系
区别:
- Vector底层扩容长度为原数组2倍,线程安全,效率低
- ArrayList底层扩容长度为原数组1.5倍,线程不安全,效率高
联系:
- 底层都是数组,具有数组的优缺点(查询快,增删慢)
- 都继承了AbstractList类并实现List接口

add方法源码简化
public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{protected Object[] elementData; //数组protected int elementCount;//元素个数protected int capacityIncrement;//扩容数量private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;public Vector() {//创建时默认数组长度为10,ArrayList创建出来的时候是空数组,第一次调用add才会创建长度10的数组this(10); }public Vector(int initialCapacity) {this(initialCapacity, 0);}public Vector(int initialCapacity, int capacityIncrement) {super();this.elementData = new Object[initialCapacity];this.capacityIncrement = capacityIncrement;}public Vector(Collection<? extends E> c) {Object[] a = c.toArray();elementCount = a.length;if (c.getClass() == ArrayList.class) {elementData = a;} else {elementData = Arrays.copyOf(a, elementCount, Object[].class);}}//为什么Vector是线程安全的,因为方法上都有synchronized关键字进行同步public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;}private void ensureCapacityHelper(int minCapacity) {// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;//这里就是两倍扩容的代码了,在capacityIncrement一直为0的时候,就是oldCapacity+oldCapacityint newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);elementData = Arrays.copyOf(elementData, newCapacity);}}
三、 LinkedList实现类
1. 简要原理图

2. 源码分析(简化版)
JDK1.7和JDK1.8的LinkedList的源码是一致的
public class LinkedList<E>{//E是一个泛型,具体的类型要在实例化的时候才会最终确定transient int size = 0;//集合中元素的数量//Node的内部类private static class Node<E> {E item;//当前元素Node<E> next;//指向下一个元素地址Node<E> prev;//上一个元素地址Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}transient Node<E> first;//链表的首节点ransient Node<E> last;//链表的尾节点//空构造器:public LinkedList() {}//添加元素操作:public boolean add(E e) {linkLast(e);return true;}void linkLast(E e) {//添加的元素efinal Node<E> l = last;//将链表中的last节点给l 如果是第一个元素的话 l为null//将元素封装为一个Node具体的对象:final Node<E> newNode = new Node<>(l, e, null);//将链表的last节点指向新的创建的对象:last = newNode;if (l == null)//如果添加的是第一个节点first = newNode;//将链表的first节点指向为新节点else//如果添加的不是第一个节点 l.next = newNode;//将l的下一个指向为新的节点size++;//集合中元素数量加1操作modCount++;}//获取集合中元素数量public int size() {return size;}//通过索引得到元素:public E get(int index) {checkElementIndex(index);//健壮性考虑return node(index).item;}Node<E> node(int index) {//如果index在链表的前半段,那么从前往后找if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {//如果index在链表的后半段,那么从后往前找Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}}
}
四、其他问题
1.ArrayList源码和Vector源码中的都会有一个成员变量private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,这个数组容量上限MAX_ARRAY_SIZE为什么要-8?
其实源码中成员变量上面就有英文解释,简要回答就是保证跨平台,有些虚拟机数组会有会有保留字,出于安全性和健壮性考虑所以做了长度限制。可参考以下博客说明。
https://blog.csdn.net/qq_44734036/article/details/118982215
为什么是-8可以参考以下博客说明。
https://blog.csdn.net/fisherish/article/details/117134717
实际上ArrayList能不能直接到达Integer.MAX_VALUE这个,我认为是可以的,ArrayList源码扩容函数里面实际是允许的,只是一开始new ArrayList<>(Integer.MAX_VALUE);这个会报错,但是如果进行扩容操作的话能达到。这个只是从源码上进行的推论,没有实际能验证过。
以下为源码说明:

相关文章:
Java集合——List接口学习总结
一、ArrayList实现类 1. 常用方法 增加:add(int index, E element)删除:remove(int index) remove(Object o)修改:set(int index, E element)查看:get(int index)判断:常用遍历方式://List集合 遍历&…...
低代码(三)低代码平台前端技术组件选型1.0(前端)
目前国内主流的低代码开发平台有:金蝶、用友、宜搭、云程、简道云、明道云、氚云、伙伴云、道一云、JEPaaS、华炎魔方、搭搭云、JeecgBoot 、RuoYi等。这些平台各有优劣势,定位也不同,用户可以根据自己需求选择。如果企业想自主可控ÿ…...
代码随想录算法训练营第35天|860.柠檬水找零,406.根据身高重建队列,452. 用最少数量的箭引爆气球
代码随想录算法训练营第35天|860.柠檬水找零,406.根据身高重建队列,452. 用最少数量的箭引爆气球860.柠檬水找零406. 根据身高重建队列452. 用最少数量的箭引爆气球860.柠檬水找零 题目链接:860.柠檬水找零,难度:简单…...
C++整人代码,十分朴实但威力无穷,让你对cout怀疑人生,整死你的同学
cout人人皆知 /a 只是让电脑响个铃 直接上个简单的代码 #include<iostream> using namespace std; int main() {while(1)cout<<"\a"; }最后普及一下: 控制符的作用有: setbase(n) 以n进制方式输出(n8,10,16) setfill(ch) 设置…...
【Spring Cloud Alibaba】12.定时任务(xxl-job)
文章目录简介什么是xxl-job调度中心执行器官方架构图相关地址环境要求配置调度中心下载源码目录说明初始化数据库源码方式docker方式测试集群(可选)配置执行器pom.xmlapplication.propertiesXxlJobExecutorApplication.java执行器组件配置创建定时任务任…...
GDB core dump分析
基本知识 Linux core dump:一般称之为核心转储、内核转储,我们统称为转储文件。是某个时刻某个进程的内存信息映射,即包含了生成转储文件时该进程的整个内存信息以及寄存器等信息。转储文件可以是某个进程的,也可以是整个系统的。…...
Leetcode.111 二叉树的最小深度
题目链接 Leetcode.111 二叉树的最小深度 easy 题目描述 给定一个二叉树,找出其最小深度。 最小深度是从 根节点 到 最近叶子节点 的 最短路径上的节点数量。 说明: 叶子节点是指没有子节点的节点。 示例 1: 输入:root [3,9,20,null,nul…...
【RP-RV1126】SDK编译常用记录
文章目录一、单独编译1.1 单独配置编译kernel1.2 单独编译配置Buildroot1.3 单独编译rkmedia1.3.1 添加自己的rkmedia代码文件荣品的RV1126。一、单独编译 如果执行 build.sh 运行完成后没有在 rockdev/ 目录下生成镜像文件,请执行: ./build.sh firmwa…...
【操作系统复习】第5章 存储器管理
存储器的层次结构 存储层次 ➢ CPU寄存器 ➢ 主存:高速缓存、主存储器、磁盘缓存 ➢ 辅存:固定磁盘、可移动介质 层次越高,访问速度越快,价格也越高,存储容量也最小 寄存器和主存掉电后存储的信息不再存在&a…...
Python人工智能在气象中的实践技术应用
专题一 Python 和科学计算基础 1.1 Python 入门和安装 1.1.1 Python 背景及其在气象中的应用 1.1.2 Anaconda 解释和安装以及 Jupyter 配置1.1.3 Python 基础语法 1.2 科学数据处理基础库 1.2.1 Numpy 库1.2.2 Pandas 库1.2.3 Scipy 库 1.2.4 Matplotlib 和 Cartopy 库 …...
libcurl库的安装及使用说明
目录 一 libcurl库安装 ① 下载网址 ② libcurl库安装步骤 ③ libcurl等第三方库的通用编译方法 二 调用libcurl编程访问百度主页 ① 代码说明 ② 编译说明 ③ 执行说明 三 libcurl的使用说明 ① curl相关函数简介 ② curl_easy_setopt函数部分选项介绍 ③…...
【JAVAEE】手把手教学多线程,包教包会~
线程与进程为了实现多个任务并发执行的效果,人们引进了进程。何谓进程?我们电脑上跑起来的每个程序都是进程。每一个进程启动,系统会为其分配内存空间以及在文件描述符表上有所记录等。进程是操作系统进行资源分配的最小单位,这意…...
基于ChatGPT API的PC端软件开发过程遇到的问题的分析
如果喜欢本文章,记得收藏哦! 关注我,一起学Java。 一、基于ChatGPT API的PC端软件开发过程遇到的问题的分析 最近这个OpenAI公司推出的GPT-4.0模型真是太火了。当然由于OpenAI目前还没有正式全面对外开放GPT-4.0 API,所以本次使用…...
啥是插入排序 ?
一、概述 排序是算法中的一部分。所以我们学习排序也是算法的入门,为了能让大家感受到排序是算法的一部分,我举个例子证明一下:比如麻将游戏,发完牌之后需要对手上的牌进行排序,大家想想,麻将排序如何排呢…...
华为OD机试题 Q2 押题【贪心的商人 or 最大利润】用 C++ 编码,速通
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:贪心的商人 or 最大利润 题目…...
spring框架注解
3.Spring有哪些常用注解呢? Spring常用注解 Web: Controller:组合注解(组合了Component注解),应用在MVC层(控制层)。 RestController:该注解为一个组合注解,相当于Con…...
前端如何处理文本溢出
前言 在现代网页设计中,文本是网页中最重要的内容之一。然而,当文本超出其容器的大小时,会发生文本溢出的问题。文本溢出不仅会影响网页的视觉效果,还会影响网页的可读性和可用性。在前端开发中,解决文本溢出的问题是…...
vue elementUI select下拉框设置默认值(赋值)失败
vue elementUI select下拉框设置默认值 要为select下拉框设定默认值,只需要把 v-model 绑定的值和你想要选中 option 的 value 值设置一样即可。 下面上代码: html部分代码: <el-select v-model"valuetype" change"ch…...
TensorRT创建Engine并推理engine
1. 验证集数据集 Class Images Labels P R mAP.5 mAP.5:.95: 100%|██████████| 84/all 1000 28423 0.451 0.374 0.376 0.209pedestrians 1000 17833 0.737 0.855 0.88 …...
生成式人工智能所面临的问题有哪些?
在生成式人工智能中工作需要混合技术、创造性和协作技能。通过发展这些技能,您将能够在这个令人兴奋且快速发展的领域应对具有挑战性的问题。 生成式人工智能是指一类机器学习技术,旨在生成与训练数据相似但不完全相同的新数据。 换句话说,…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
