jdk1.8-ArrayList源码分析
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
继承AbstracList抽象父类
private static final long serialVersionUID = 8683452581122892189L;
/** * Default initial capacity. */private static final int DEFAULT_CAPACITY = 10;
/** * Shared empty array instance used for empty instances. * */private static final Object[] EMPTY_ELEMENTDATA = {};
/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */transient Object[] elementData; // non-private to simplify nested class access
/** * The size of the ArrayList (the number of elements it contains). * * @serial */private int size;
/** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */public ArrayList(int initialCapacity) {if (initialCapacity > 0) {//如果传进来的长度大于0,则直接初始化Object[]传进来的长度数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) {//如果长度为0则让数组等于Object[] EMPTY_ELEMENTDATA = {};空数组 this.elementData = EMPTY_ELEMENTDATA; } else {//抛错 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }}
/** * 不传参,初始化一个空数组,与上面空数组不为同一个 */public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
/** * 当传递的参数为集合类型时,会把集合类型转化为数组类型,并赋值给elementData */public ArrayList(Collection<? extends E> c) {//先转为数组 elementData = c.toArray(); //参数长度不为0 if ((size = elementData.length) != 0) {//如果没有成功转为Object型数组,Object[].class得到的是class [Ljava.lang.Object; if (elementData.getClass() != Object[].class)//那么就进行拷贝 elementData = Arrays.copyOf(elementData, size, Object[].class); } else {// replace with empty array. this.elementData = EMPTY_ELEMENTDATA; }}
/** * 将指定的元素添加到此列表的尾部。 */public boolean add(E e) {//确保数组有合适的大小 ensureCapacityInternal(size + 1); // Increments modCount!! //放入对应的数组下表 elementData[size++] = e; return true;}
private void ensureCapacityInternal(int minCapacity) {//如果elementData数组等于默认空元素数组则进入下面的逻辑(ps:我们假设是不传参初始化的ArrayList,所以会进入下面括号内的逻辑) if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//DEFAULT_CAPACITY=10,比较两者数的大小,较大值 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //将参数继续传入该函数 ensureExplicitCapacity(minCapacity);}
分析:这里minCapacity传入的时候为1,经过比较取大值后,minCapacity为10。
private void ensureExplicitCapacity(int minCapacity) {//记录修改次数加1 modCount++; //当前传入minCapacity长度大于当前数组长度 if (minCapacity - elementData.length > 0)//将参数传入该函数 grow(minCapacity);}
分析:modCount++,然后执行grow()方法
/** * 真正执行数组扩容的方法,先判断扩容的长度,最后执行Arrays.copyOf扩容 */private void grow(int minCapacity) {//当前旧的数组长度 int oldCapacity = elementData.length; //新数组长度等于 = 旧数组长度 + 旧数组长度除以2 int newCapacity = oldCapacity + (oldCapacity >> 1); //小于传进来的参数则等于传进来的参数 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //大于int的最大长度(2^31 - 1) 再减去8长度,传入minCapacity参数,执行hugeCapacity()方法,指定新容量 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //执行Arrays.copyOf将旧数组扩容成新数组长度为newCapacity elementData = Arrays.copyOf(elementData, newCapacity);}
分析:在我们假设的前提下,minCapacity当前等于10,当前oldCapacity为0。
/** * 将指定的元素添加到此列表的尾部。 */public boolean add(E e) {//确保数组有合适的大小 ensureCapacityInternal(size + 1); // Increments modCount!! // elementData[size++] = e; return true;}
分析:这里现在ensureCapacityInternal()传入的参数为2
private void ensureCapacityInternal(int minCapacity) {//如果elementData数组等于默认空元素数组则进入下面的逻辑(ps:我们前面假设是不传参初始化的ArrayList,所以会进入下面括号内的逻辑) if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//DEFAULT_CAPACITY=10,比较两者数的大小,较大值 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //将参数等于10继续传入该函数 ensureExplicitCapacity(minCapacity);}
分析:第二次添加,那么minCapacity就等于2。也就是minCapacity可以理解为当前元素个数。
private void ensureExplicitCapacity(int minCapacity) {//记录修改次数加1 modCount++; //当前传入minCapacity长度大于当前数组长度 if (minCapacity - elementData.length > 0)//将参数传入该函数 grow(minCapacity);}
分析:到这里就是记录修改次数加1了,2小于当前数组长度10,不满足,不往下执行。
private void ensureExplicitCapacity(int minCapacity) {//记录修改次数加1 modCount++; //当前传入minCapacity长度大于当前数组长度 if (minCapacity - elementData.length > 0)//将参数传入该函数 grow(minCapacity);}
分析:我们从这里很容易就看出,第二次准备扩容时,elementData.length当前的数组长度是10。当前的元素个数minCapacity为11个时,放不下,也就是数组个数已经满了,开始扩容。
/** * 真正执行数组扩容的方法,先判断扩容的长度,最后执行Arrays.copyOf扩容 */private void grow(int minCapacity) {//当前旧的数组长度 int oldCapacity = elementData.length; //新数组长度等于 = 旧数组长度 + 旧数组长度除以2 (也就是1.5倍) int newCapacity = oldCapacity + (oldCapacity >> 1); //小于传进来的参数则等于传进来的参数 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //大于int的最大长度(2^31 - 1)再减去8长度,传入minCapacity参数,执行hugeCapacity()方法 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //执行Arrays.copyOf将旧数组扩容成新数组长度为newCapacity elementData = Arrays.copyOf(elementData, newCapacity);}
分析:此时,newCapactiy值等于旧数组的1.5倍,然后进行复制扩容。也就是说,第二次包括以后数组满了再扩容,在满足没有超过MAX_ARRAY_SIZE的前提下每次数组扩容都是扩容为原来数组长度的1.5倍长。
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE :MAX_ARRAY_SIZE;}
MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
//当前传入minCapacity长度大于当前数组长度if (minCapacity - elementData.length > 0)//将参数传入该函数 grow(minCapacity);
/** * 通过移动数组后面所有的元素覆盖当前索引元素,从而达到删除当前元素的效果 * 数组最后一个索引值置为null,说明ArrayList可以查找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); //数组最后一个索引值置为null,并且索引减1 elementData[--size] = null; // clear to let GC do its work return oldValue;}
/** * 因为底层是数组,所以set时,可以直接根据索引进行覆盖旧的值 */public E set(int index, E element) {//检查索引是否越界 rangeCheck(index); E oldValue = elementData(index); //直接根据索引覆盖值 elementData[index] = element; return oldValue;}
/** * 从首部开始查找,返回第一个值相同的索引,遍历完没找到返回-1 */public int indexOf(Object o) {//传进来的值为null,从首部开始遍历数组,找到第一个为null的值,返回当前索引 if (o == null) {for (int i = 0; i < size; i++)if (elementData[i]==null)return i; } else {//不为空,从首部遍历,返回第一个值相同的下标索引 for (int i = 0; i < size; i++)if (o.equals(elementData[i]))return i; }//没找到返回-1 return -1;}
转载于:https://www.cnblogs.com/lizb0907/p/10342760.html
jdk1.8-ArrayList源码分析相关推荐
- 【Java源码分析】Java8的ArrayList源码分析
Java8的ArrayList源码分析 源码分析 ArrayList类的定义 字段属性 构造函数 trimToSize()函数 Capacity容量相关的函数,比如扩容 List大小和是否为空 con ...
- ArrayList源码分析与手写
本节主要分析JDK提供的ArrayList的源码,以及与自己手写的ArrayList进行对比. ArrayList源码分析 构造方法 private static final int DEFAULT_ ...
- Java集合Collection源码系列-ArrayList源码分析
Java集合系列-ArrayList源码分析 文章目录 Java集合系列-ArrayList源码分析 前言 一.为什么想去分析ArrayList源码? 二.源码分析 1.宏观上分析List 2.方法汇 ...
- ArrayList 源码分析
公众号原文:ArrayList 源码分析 博客原文:ArrayList 源码分析 以下源码分析使用的 Java 版本为 1.8 1. 概览 ArrayList 是基于数组实现的,继承 Abstract ...
- Java源码详解五:ArrayList源码分析--openjdk java 11源码
文章目录 注释 类的继承与实现 构造函数 add操作 扩容函数 remove函数 subList函数 总结 本系列是Java详解,专栏地址:Java源码分析 ArrayList 官方文档:ArrayL ...
- java list addall源码_Java集合:ArrayList源码分析
其实我看到已有很多大佬写过此类文章,并且写的也比较清晰明了,那我为何要再写一遍呢?其实也是为了加深本身的印象,巩固本身的基础html (主要是不少文章没有写出来我想知道的东西!!!!!!!)java ...
- 面试必会之ArrayList源码分析手写ArrayList
作者:Java知音-微笑面对生活 简介 ArrayList是我们开发中非常常用的数据存储容器之一,其底层是数组实现的,我们可以在集合中存储任意类型的数据,ArrayList是线程不安全的,非常适合用于 ...
- Java中ArrayList源码分析
一.简介 ArrayList是一个数组队列,相当于动态数组.每个ArrayList实例都有自己的容量,该容量至少和所存储数据的个数一样大小,在每次添加数据时,它会使用ensureCapacity()保 ...
- 扩容是元素还是数组_02 数组(附ArrayList源码分析)
定义 用一组连续的内存空间存储一组具有相同类型的数据的线性表数据结构. 优势 支持通过下标快速的随机访问数据,时间复杂度为O(1). 劣势 通常情况下,插入和删除效率低下,每次操作后,需要进行后续元素 ...
- ArrayList源码分析(基于JDK1.6)
不积跬步,无以至千里:不积小流,无以成江海.从基础做起,一点点积累,加油! <Java集合类>中讲述了ArrayList的基础使用,本文将深入剖析ArrayList的内部结构及实现原理,以 ...
最新文章
- mysql索引 钱缀_mysql字符串前缀索引
- 当git上只做文件大小写重命名的修改时,如何躲坑...
- C++服务器设计(七):聊天系统服务端实现
- 分享下自己编译 XBMC 的过程(zhuan)
- 阿里云Redis混合存储典型场景:如何轻松搭建视频直播间系统
- 微信公众号中选择时间css,微信公众号到底应该几点推文?
- 如何订阅MVP on dot NET(或其它播客) - iTunes版
- ArrayList(Map(k v))相关操作和遍历
- shell 脚本 简易for循环 脚本
- Ext的组件模型印象
- 文档化ring3 api列举驱动列表 --- 做了一些重构。(解决内存泄漏问题)
- oracle linux下数据迁移到不同服务器
- 如何用EasyRecovery找回回收站信息(附注册机下载地址)
- linux ubuntu下网络调试助手(GUI)工具
- JQuery Mobile试试水
- 一步一步学网络爬虫(从python到scrapy)
- 数据库--视图的基本概念以及作用
- Java语言每日一练—第14天:银行收入计算
- 26个英语单词起源(百度百科+巴士英语)
- python数组堆叠,堆叠数组-python数据处理
热门文章
- word中的表格如何修改文字方向
- 从零搭建一个基于React+Nextjs的SSR网站(四):如何搭建服务器并部署Nextjs项目
- TCP/IP——网络层
- 基于深度学习的服装图像分类与检索
- python pymssql — pymssql模块使用指南
- pymssql连mysql_Python利用pymssql访问mysql数据库
- java语言产生_在java语言中产生正确的行为
- 露营“卖水人”,还能火多久?
- element UI-表格数据上下移动功能
- java sql绑定_在JAVA 源程序中编写SQL语句时使用ORACLE 绑定变量