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 …...

生成式人工智能所面临的问题有哪些?
在生成式人工智能中工作需要混合技术、创造性和协作技能。通过发展这些技能,您将能够在这个令人兴奋且快速发展的领域应对具有挑战性的问题。 生成式人工智能是指一类机器学习技术,旨在生成与训练数据相似但不完全相同的新数据。 换句话说,…...

代码随想录算法训练营第四十三天 | 1049. 最后一块石头的重量 II、494. 目标和、474. 一和零
打卡第43天,01背包应用。 今日任务 1049.最后一块石头的重量 II494.目标和474.一和零 1049. 最后一块石头的重量 II 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合,从中选出任意两块石头࿰…...

PostCSS 让js可以处理css
GitHub 中文readmie PostCSS 中文网(建设中) PostCSS 不是样式预处理器 是 CSS 语法转换的工具,但不严格遵循css规范,只要符合css语法规则就可以被处理。这也让提前实现新提案成为可能。 使用 webpack 中使用 postcss-loader …...

【C语言进阶:自定义类型详解】位段
本节重点内容: 什么是位段位段的内存分配位段的跨平台问题位段的应用⚡什么是位段 位段的声明和结构是非常类似的,但是有两个不同: 位段的成员必须是 int、unsigned int 或signed int 。位段的成员名后边有一个冒号和一个数字。 struct A…...

十三、RNN循环神经网络实战
因为我本人主要课题方向是处理图像的,RNN是基本的序列处理模型,主要应用于自然语言处理,故这里就简单的学习一下,了解为主 一、问题引入 已知以前的天气数据信息,进行预测当天(4-9)是否下雨 日期温度气压是否下雨4-…...

五子棋透明棋盘界面设计(C语言)
五子棋透明棋盘设计,漂亮的界面制作。程序设置双人对奕,人机模式,对战演示三种模式。设置悔棋,记录功能,有禁手设置。另有复盘功能设置。 本文主要介绍透明的玻璃板那样的五子棋棋盘的制作。作为界面设计,…...

Redis第六讲 Redis之List底层数据结构实现
List数据结构 List是一个有序(按加入的时序排序)的数据结构,Redis采用quicklist(双端链表) 和 ziplist 作为List的底层实现。可以通过设置每个ziplist的最大容量,quicklist的数据压缩范围,提升数据存取效率 list-max-ziplist-size -2 // 单个ziplist节点最大能存储 8kb ,…...

电子学会2023年3月青少年软件编程python等级考试试卷(四级)真题,含答案解析
目录 一、单选题(共25题,共50分) 二、判断题(共10题,共20分) 三、编程题(共3题,共30分)...

【MATLAB】一篇文章带你了解beatxbx工具箱使用
目录 一篇文章带你了解beatxbx工具箱使用 一篇文章带你了解beatxbx工具箱使用 clc;clear; tic; % step1 初始化 % 个体数量 NIND = 35; % 最大遗传代数 MAXGEN = 180; % 变量的维数 NVAR = 2; % 变量的二进制位数 % 上下界 bounds=[-10 10-10 10]; precision=0.0001; %运算精度…...

【LinuxC Sqlite数据库小项目】基于Sqlite的打卡系统------适合初学者练手的小项目
最近小哥老是想浪,不想好好学习,这不行啊,得想点办法,多少做点努力,于是就自己给自己写了个打卡程序; 该程序基于Sqlite数据库,实现一个简单的打卡功能,该函数具有自动初始化的功能…...

在掌握C#基础上再学习C语言
C#和C语言虽然名字相似,但它们在很多方面都有很大的区别。 首先,C#是一种面向对象的语言,而C语言是过程化的语言。这意味着C#具有更丰富的语言特性,如类、接口、继承和多态性等,而C语言则更侧重于直接对计算机硬件进行…...