ArrayList集合简单源码分析+一道面试题

ArrayList类中的属性

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{@java.io.Serialprivate static final long serialVersionUID = 8683452581122892189L;/*** DEFAULT_CAPACITY表示集合默认的初始容量*/private static final int DEFAULT_CAPACITY = 10;/*** 空数组EMPTY_ELEMENTDATA,在使用有参构造方法时传递0进去,创建ArrayList集合时就会使用这个数组*/private static final Object[] EMPTY_ELEMENTDATA = {};/**默认容量的空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA*/private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/*** 集合中真实存储元素的数组elementData*/transient Object[] elementData; // non-private to simplify nested class access/*** 集合中元素的个数* @serial*/private int size;    //int类型的成员变量初始值为0

ArrayList类中的构造方法

  • 无参构造方法:相当于内部创建了一个空的数组

    /*** Constructs an empty list with an initial capacity of ten.*/public ArrayList() {//将成员变量中的 默认容量的空数组 赋值给 真实存储元素的数组//this.elementData = {}this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
  • 有参构造方法分析:内部创建了一个指定长度的数组 并赋值给了elementData

main方法:

   
public class Test {public static void main(String[] args) {ArrayList list = new ArrayList(20);  //创建一个指定初始容量为20的ArrayList集合}
}
  • 有参构造方法源码:

    public ArrayList(int initialCapacity) {   // initialCapacity=20if (initialCapacity > 0) {//内部创建了一个长度为20的数组,并赋值给this.elementDatathis.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) {//如果传递的是0,就将空数组EMPTY_ELEMENTDATA 赋值给 this.elementDatathis.elementData = EMPTY_ELEMENTDATA;} else {//如果在构造方法中传递负数,就会抛出如下异常throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}

无参构造方法创建的对象:添加方法

第一次添加数据:

在main方法中:

import java.util.ArrayList;public class Test {public static void main(String[] args) {ArrayList list1 = new ArrayList();list1.add(1);   //添加第一个元素进去}
}

第一次添加数据的流程:

  • 一级一级的调用下去

    public boolean add(E e) {//确定内部数组的容量          0 + 1ensureCapacityInternal(size + 1);  // Increments modCount!!//将要添加的元素 添加 到数组中有数据的下一个位置elementData[size++] = e;        return true;}-------------------------------------------------------------------------------------//minCapacity:是ArrayList集合需要的最小容量(暂时这样理解)private void ensureCapacityInternal(int minCapacity) { // minCapacity=1if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {   // {}=={}// 						10					1minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//通过得到的minCapacity去确定数组的长度ensureExplicitCapacity(minCapacity);}-------------------------------------------------------------------------------------private void ensureExplicitCapacity(int minCapacity) {  //minCapacity=10modCount++;   //记录对当前集合操作的次数// 集合需要的最小容量minCapacity 大于 elementData数组的长度时就会扩容(也就是当集合内部的数组不够存储元素时才会扩容)    10-0if (minCapacity - elementData.length > 0)//扩容grow(minCapacity);}-------------------------------------------------------------------------------------private void grow(int minCapacity) {    //minCapacity=10int oldCapacity = this.elementData.length;   //原来的数组长度为0int newCapacity = oldCapacity + (oldCapacity >> 1);    //新的容量0if (newCapacity - minCapacity < 0) {   //0 - 10 < 0,执行newCapacity = minCapacity;     //newCapacity = 10 }if (newCapacity - 2147483639 > 0) {  //10 - 21亿 < 0newCapacity = hugeCapacity(minCapacity);}//将空数组{} 复制到 一个长度为 10的新数组中,然后将新的数组赋值给elementData。此时size还是0this.elementData = Arrays.copyOf(this.elementData, newCapacity);}//然后回去继续向下执行,执行的就是向数组中添加数据的操作public boolean add(E e) {//确定内部数组的容量        0 + 1ensureCapacityInternal(size + 1);  // 0 + 1//将要添加的元素 添加 到数组中有数据的下一个位置elementData[size++] = e;        //elementData={e,,,,,,,,,} 数组的length=10,size=1return true;}
第二次添加元素:
  • 第二次添加元素也还不会扩容

main方法中:

package string.demo;import java.util.ArrayList;public class Test {public static void main(String[] args) {ArrayList list1 = new ArrayList();list1.add(1);list1.add(2);  //第二次添加数据}
}

第二次添加元素的执行流程:

    public boolean add(E e) {//确定内部数组的容量 1 + 1 = 2ensureCapacityInternal(size + 1);  // Increments modCount!!//将要添加的元素 添加 到数组中有数据的下一个位置elementData[size++] = e;        return true;}-------------------------------------------------------------------------------------private void ensureCapacityInternal(int minCapacity) { // minCapacity=2if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {   // {1,,,...}!={} 不执行语句体minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//确定数组的容量ensureExplicitCapacity(minCapacity);}-------------------------------------------------------------------------------------private void ensureExplicitCapacity(int minCapacity) {  minCapacity=2modCount++;   //记录对当前集合操作的次数 modCount=modCount + 1   (modCount=2)// 集合需要的最小容量minCapacity 大于 elementData数组的长度时就会扩容    2 - 10 < 0:不执行if语句体,也就是不扩容if (minCapacity - elementData.length > 0)//扩容grow(minCapacity);}
-----------------------------------------------------------------------------------------//然后回去继续向下执行,执行的就是向数组中添加数据的操作public boolean add(E e) {//确定内部的容量 0 + 1ensureCapacityInternal(size + 1);  // Increments modCount!!//将要添加的元素 添加 到数组中有数据的下一个位置elementData[size++] = e;     //  {1,2,.....}  此时:数组length = 10,元素个size=2return true;}
第十一次添加元素
  • 此时ArrayList集合内部的数组不够存储新元素了,会扩容

    public boolean add(E e) {//此时elementData数组:{1,2,3,4,5,6,7,8,9,10}   size = 10 , length = 10ensureCapacityInternal(size + 1);  // 10 + 1 =11//将要添加的元素 添加 到数组中有数据的下一个位置elementData[size++] = e;    return true;}-------------------------------------------------------------------------------------private void ensureCapacityInternal(int minCapacity) {  minCapacity = 11if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {   minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//确定数组的容量ensureExplicitCapacity(minCapacity);  //11}-------------------------------------------------------------------------------------private void ensureExplicitCapacity(int minCapacity) {  minCapacity=11modCount++;   //记录对当前集合操作的次数 modCount=modCount + 1   (modCount=11)// minCapacity 大于 elementData数组的长度时就会扩容    11-10 > 0:扩容if (minCapacity - elementData.length > 0)//扩容grow(minCapacity);}-------------------------------------------------------------------------------------private void grow(int minCapacity) {    //minCapacity=11//oldCapacity = 10int oldCapacity = this.elementData.length;  //获取原容量的长度//新的容量newCapacity是在原容量的基础上扩容1.5倍//newCapacity = 10 + 5int newCapacity = oldCapacity + (oldCapacity >> 1);    if (newCapacity - minCapacity < 0) {   //15 - 11 > 0,不执行newCapacity = minCapacity;     }if (newCapacity - 2147483639 > 0) {  //15 - 21亿 < 0,不执行newCapacity = hugeCapacity(minCapacity);}//将{1,2,3,4,5,6,7,8,9,10}长度为10的数组 复制到 长度为 15的新数组中,然后将新的数组赋值给elementData。this.elementData = Arrays.copyOf(this.elementData, newCapacity);}//再回去执行向数组中添加元素的逻辑public boolean add(E e) {//此时elementData数组:{1,2,3,4,5,6,7,8,9,10}   size = 10 , length = 10ensureCapacityInternal(size + 1);  // 10 + 1 =11//将要添加的元素 添加 到数组中有数据的下一个位置{1,2,3,4,5,6,7,8,9,10,11,,,,} 此时size=11 length=15elementData[size++] = e;    return true;}

有参构造方法创建的对象:添加方法

执行时调用的方法都和无参的一样

main方法:

package string.demo;import java.util.ArrayList;public class Test {public static void main(String[] args) {//elementData = {,,,,,} 长度为20的数组ArrayList list2 = new ArrayList(20);list2.add(1);}
}

添加数据:

    public boolean add(E e) {//确定内部的容量 0 + 1ensureCapacityInternal(size + 1);  // Increments modCount!!//将要添加的元素 添加 到数组中有数据的下一个位置elementData[size++] = e;        return true;}-------------------------------------------------------------------------------------private void ensureCapacityInternal(int minCapacity) { // minCapacity=1if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {   // elementData != {}不执行minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//确定数组的容量ensureExplicitCapacity(minCapacity);}-------------------------------------------------------------------------------------private void ensureExplicitCapacity(int minCapacity) {  //minCapacity=1modCount++;   //记录对当前集合操作的次数// 集合中的元素个数 等于数组的长度时才会扩容   if (minCapacity - elementData.length > 0)  //1 - 20 < 0 不扩容//扩容grow(minCapacity);}-------------------------------------------------------------------------------------private void grow(int minCapacity) {      int oldCapacity = this.elementData.length;   int newCapacity = oldCapacity + (oldCapacity >> 1);    //新的容量if (newCapacity - minCapacity < 0) {   newCapacity = minCapacity;     }if (newCapacity - 2147483639 > 0) {  newCapacity = hugeCapacity(minCapacity);}this.elementData = Arrays.copyOf(this.elementData, newCapacity);}

ArrayList中的get方法

main方法:

package string.demo;import java.util.ArrayList;public class Test {public static void main(String[] args) {ArrayList list1 = new ArrayList();list1.add(1);list1.add(2);list1.get(0);}
}

ArrayList中的get方法源码:

    public E get(int index) {//检查下标是否合法Objects.checkIndex(index, size);//通过下标获取数组中对应的元素return elementData(index);}

ArrayList中的set方法

main方法:

package string.demo;import java.util.ArrayList;public class Test {public static void main(String[] args) {ArrayList list1 = new ArrayList();list1.add(1);list1.add(2);list1.set(1,"aaa");}
}

ArrayList中的set方法源码:

jdk17:

    public E set(int index, E element) {Objects.checkIndex(index, size); //检查下标是否合法E oldValue = elementData(index);  //获取原来下标对应的元素elementData[index] = element;   //将新的元素赋值给数组中对应的下标位置上return oldValue;   //返回原来的值}

过去版本的jdk:

    public E set(int index, E element) {rangeCheck(index);  //检查下标是否合法E oldValue = elementData(index);  //获取原来下标对应的元素elementData[index] = element;   //将新的元素赋值给数组中对应的下标位置上return oldValue;   //返回原来的值}

ArrayList中的remove方法:

main方法:

package string.demo;import java.util.ArrayList;public class Test {public static void main(String[] args) {ArrayList list1 = new ArrayList();list1.add(1);list1.add(2);list1.add(3);list1.add(4);list1.add(5);list1.add(6);list1.add(7);list1.add(8);list1.add(9);list1.remove(3);}
}

ArrayList中的remove方法源码:

过去版本的jdk:

    public E remove(int index) {     index=3rangeCheck(index);    //检查下标modCount++;  //记录修改当前集合的次数E oldValue = elementData(index);  //获取原来下标上的元素//计算要移动的元素的个数{1,2,3,4,5,6,7,8,9} //{1,2,3,5,6,7,8,9,null}int numMoved = size - index - 1;    //9 - 3 - 1 = 5if (numMoved > 0)System.arraycopy(elementData   //原数组, index+1   //开始移动的下标 5,6,7,8,9, elementData   //目标数组, index,   //开始的下标numMoved   //要移动的元素个数);//{1,2,3,5,6,7,8,9,null}                        elementData[--size] = null; // clear to let GC do its workreturn oldValue;}
    public E remove(int index) {Objects.checkIndex(index, size);final Object[] es = elementData;@SuppressWarnings("unchecked") E oldValue = (E) es[index];fastRemove(es, index);return oldValue;}

TreeSet的实现原理

在构造方法中创建了一个TreeMap实例

    public TreeSet() {this(new TreeMap<>());}

在调用add方法时,本质上是调用了TreeMap的put方法

    public boolean add(E e) {return m.put(e, PRESENT)==null;}

Hashset的实现原理

在其无参构造方法中实例化了一个HashMap实例

    public HashSet() {map = new HashMap<>();}

在调用add方法的时候本质上是调用了HashMap的put方法

    public boolean add(E e) {return map.put(e, PRESENT)==null;}

去除ArrayList集合中重复的元素

方式一:

会创建一个新的ArrayList集合

package collection;import java.util.ArrayList;public class ListTest {public static void main(String[] args) {ArrayList list1 = new ArrayList();list1.add(1);list1.add(3);list1.add(2);list1.add(null);list1.add("张三");list1.add("李四");list1.add(3);System.out.println(list1);//新创建一个List集合存储去重后的元素ArrayList list2 = new ArrayList();for (Object o : list1){   //遍历原来的集合if (!list2.contains(o)){   //判断新的集合中是否包含list1中的元素,如果没有则1添加到list2中list2.add(o);}}System.out.println(list2);}
}

运行结果:

[1, 3, 2, null, 张三, 李四, 3]

[1, 3, 2, null, 张三, 李四]

方式二

不创建新集合,使用选择排序的思想去除重复元素

package collection;import java.util.ArrayList;public class ListTest {public static void main(String[] args) {ArrayList list1 = new ArrayList();list1.add(1);list1.add(3);list1.add(2);list1.add(2);list1.add(2);list1.add("张三");list1.add("李四");list1.add(3);System.out.println(list1);for (int i = 0;i < list1.size() - 1;i++){for (int j = i + 1;j<list1.size();j++){if (list1.get(i).equals(list1.get(j))){list1.remove(j);j--;}}}System.out.println(list1);}
}

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

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

相关文章

【ASP.NET Core 基础知识】--数据库连接--数据迁移和代码优先开发

一、数据迁移 1.1 定义和用途 数据迁移是指将数据从一个存储系统、数据格式、应用程序或硬件平台转移到另一个的过程。这个过程可以涉及数据的转换、清洗和验证&#xff0c;以确保数据的完整性和一致性。一般用于如下情况&#xff1a; 系统升级&#xff1a; 当企业需要更新其…

力扣(LeetCode)227. 基本计算器 II

给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。 注意&#xff1a;不允许使用任何将字符串作为数学表达式计算的内置函数&#…

第二百八十九回

文章目录 1. 概念介绍2. 方法与细节2.1 实现方法2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何混合选择多个图片和视频文件"相关的内容&#xff0c;本章回中将介绍如何通过相机获取视频文件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. …

三角函数、反三角函数

一、三角函数 二、反三角函数&#xff1a;已知三角函数值&#xff0c;反算角度大小 因为严格单调函数才有反函数一个y对应一个x&#xff0c;显然ysinx&#xff0c;ycosx&#xff0c;ytanx在其定义域并不是严格单调&#xff0c;所以需要人为划定范围。 1. 研究yarcsinx、yarcco…

CSS优先级内容

定义CSS样式时&#xff0c;经常出现两个或多个样式规则应用在同一元素的情况&#xff0c;这时就会出现优先级的情况&#xff0c;那么应用的元素应该显示哪一个样式呢&#xff1f; 一.下面举例对优先级进行具体讲解。 p{color:red;} .blue{color:orange;} #header{color:blu…

嵌入式学习-C++-Day3

嵌入式学习-CDay3 一、思维导图 二、作业 1.设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数。 #inclu…

桌面型物联网智能机器人设计(预告)

相关资料 桌面级群控机器人CoCube探索-2022--CSDN博客 视频&#xff1a; 能&#xff01;有&#xff01;多&#xff01;酷&#xff01;CoCube桌面级群控机器人 让我看看谁在SJTU里划水… 简要介绍 设计一个桌面型物联网智能机器人&#xff0c;以ESP32芯片为核心&#xff0c;配…

Redis的SDS你了解吗?

初识SDS&#xff1a; Redis的String和其他很多编程语言中的语义相似&#xff0c;它能够表达3种值的类型&#xff1a; 1.字符串 2.整数 3.浮点数 三种类型根据具体场景由Redis完成相互之间的自动转换&#xff0c;并且根据需要选取底层的承载方式&#xff0c;Redis内部&#x…

pip 安装出现报错 SSLError(SSLError(“bad handshake

即使设置了清华源&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simplepip 安装包不能配置清华源&#xff0c;出现报错: Retrying (Retry(total2, connectNone, readNone, redirectNone, statusNone)) after connection broken by ‘SSLE…

redis为什么用单线程模型

redis为什么用单线程模型&#xff1f; 最近在深入研究redis&#xff0c;发现其中很多值得我们借鉴的思想&#xff0c;实现原理等&#xff0c;坚持每天学习一点点&#xff0c;不久就可成大佬&#xff0c;大家加油&#xff01; 言归正传&#xff0c;我来回答今天的问题&#xff…

[Python] 如何在Windows下安装图形可视化工具graphviz

什么是graphviz? Graphviz是一款开源的图形可视化工具&#xff0c;用于生成各种结构化数据的图形表示。它支持多种图形排列算法&#xff0c;可以将复杂的数据关系用图形的方式直观地展示出来。Graphviz广泛应用于软件工程、数据可视化、计算机网络以及其他领域的可视化分析中…

14 STM32标准库函数 之 实时时钟(RTC) 所有函数的介绍及使用

14 STM32标准库函数 之 实时时钟&#xff08;RTC&#xff09; 所有函数的介绍及使用 1 RTC的库函数预览1.1 函数RTC_ITConfig1.2 函数RTC_EnterConfigMode1.3 函数RTC_ExitConfigMode1.4 函数RTC_GetCounter1.5 函数RTC_SetCounter1.6 函数RTC_SetPrescaler1.7 函数RTC_ SetAla…

springboot131企业oa管理系统

企业OA管理系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了企业OA管理系统的开发全过程。通过分析企业OA管理系统管理的不足&#xff0c;创建了一个计算机管理企业OA管理系统的方案。文章介绍了企业OA管…

android 自定义键盘长按弹窗

自己记忆,下次不用找KeyboardView的onLongPress是长按监听,通过onLongPress可以获取键盘上用户长按的字母override fun onLongPress(popupKey: Keyboard.Key): Boolean {val label popupKey.labelif (!TextUtils.isEmpty(label) && popupKey.codes.get(0) ! MyKeyCode…

字符串相关的函数和内存块相关函数

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

python第五节:集合set(1)

集合是一个无序不重复元素的集合。集合中元素包含在花括号中&#xff0c;例如{a,1,tom,"xy"} 创建集合&#xff1a; 例子1&#xff1a; # set1 {a,1,tom,"xy"}set1 set()set2 set([])set3 set({})# 虽然集合用{}包含集合的元素&#xff0c;但是{}创…

前端开发有没有必要转鸿蒙开发?

前端开发有没有必要转鸿蒙开发&#xff1f;如果后面的工作中有参与鸿蒙开发的机会&#xff0c;那肯定是转呀&#xff01;毕竟多接触一些技能也不会有什么坏处。 我想说的是&#xff1a;鸿蒙替代不了前端&#xff0c;如果你目前正在从事前端开发&#xff0c;那么你完全可以将鸿蒙…

springboot 整合 ElasticSearch 方法 (二)

依赖 在pom.xml文件中需要引入3个依赖, 三个都必须有并且三个依赖的版本要一致, 不然会报错. 不一定是 7.6.1 这个版本 , 只需要保证这三个依赖的版本一致就可以了. <dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch<…

【C++入门到精通】特殊类的设计 |只能在堆 ( 栈 ) 上创建对象的类 |禁止拷贝和继承的类 [ C++入门 ]

阅读导航 引言一、特殊类 --- 不能被拷贝的类1. C98方式&#xff1a;2. C11方式&#xff1a; 二、特殊类 --- 只能在堆上创建对象的类三、特殊类 --- 只能在栈上创建对象的类四、特殊类 --- 不能被继承的类1. C98方式2. C11方法 总结温馨提示 引言 在面向对象编程中&#xff0…

制作 MSK Connect 的 Confluent Avro Converter + Debezium MySQL Connector 插件包

MSK Connect 的插件包需要将各种插件的 Jar 包及其依赖包放到一起,打成 Zip 包,上传到 S3,然后在MSK Console 上创建插件时指定好 Zip 位置即可。为了便于维护,我们不建议将各种插件的 Jar 包混在一起放入同一个文件内,最好还是按插件原来的名称单独创建文件夹,单独放置各…