ArrayList源码解析(一)
源码解析系列主要对Java的源码进行详细的说明,由于水平有限,难免出现错误或描述不准确的地方,还请大家指出。
1.位置
ArrayList位于java.util包中。
package java.util; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator;
2.变量和常量
先明确一点,ArrayList是采用Object类型的数组实现的。
ArrayList开始定义了一些常量和变量:
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity. * ArrayList初始化默认容量大小为10,见无参构造函数。
*/
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. * 默认大小的空实例共享的空数组实例。将它与EMPTY_ELEMENTDATA区分开,以便确定当添加了第一个成员之后扩充多大。 */
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. * ArrayList成员存储在这个数组缓冲区中。 * 容量大小是这个缓冲区的长度,任何elementDate==DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList * 在添加第一个成员时都会扩充到DEFAULT_CAPACITY大小,即容量为10。 */
transient Object[] elementData; // non-private to simplify nested class access
/** * The size of the ArrayList (the number of elements it contains). * ArrayList的大小,注意是实际包含的成员个数。 * @serial */ private int size;
3.构造函数
ArrayList有三个构造函数:
1)无参构造函数
/**
* Constructs an empty list with an initial capacity of ten. * 构造一个初始容量为10的空list。
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
注意,这里说构造一个初始容量为10的空list,但是赋给elementData的只是一个空的数组(原因后续说明)。
2)带指定初始容量参数的构造函数
/**
* Constructs an empty list with the specified initial capacity.
* 构造一个指定了初始容量的空list。
* @param initialCapacity the initial capacity of the list * initialCapacity指定list的初始容量
* @throws IllegalArgumentException if the specified initial capacity
* is negative * 当指定容量值为负值时,抛出IllegalArgumentException异常
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
当指定的初始容量值大于零时,新建一个大小为initialCapacity的Object数组,并赋 elementData;
当指定的初始容量值为零时,将 EMPTY_ELEMENTDATA赋给elementData。
3)使用指定Collection构造ArrayList的构造函数
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
* 构造一个包含指定Collection成员的list,成员的排列顺序与使用Collection的 * iterator返回的顺序一致。
* @param c the collection whose elements are to be placed into this list * c 指定的Collection,它的所有元素都会被放在构造的list中
* @throws NullPointerException if the specified collection is null * 当指定的Collection为null是抛出NullPointerException异常
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
既然是从指定的Collection构造ArrayList,那么成员的类型就要保持不变,即ArrayList中的成员类型与Collection中的成员类型要相同。
先利用toArray()将Collection c转换为数组,并让elementData指向这个数组;
接下来,取出elementData的成员个数并赋值给size;
如果size不为0,则判断elementData的成员类是否与Object类相同,不同则进行一次copy操作,并进行类型转换;
如果size为0,则将elementData指向EMPY_ELEMENTDATA。
4.trimToSize()方法
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance. * 将ArrayList实例的容量trim为list的当前size。 * 应用可以使用这个方法来最小化ArrayList实例的存储空间。
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
modCount用来记录修改次数,modCount主要用于多线程环境下,因为ArrayList是非线程安全的,这里使用了Fail-Fast机制。任何对ArrayList的修改都会增加modCount的值,在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。在迭代过程中,会判断 modCount是否等于 expectedModCount,如果不等,说明其他线程修改了ArrayList,就要抛出ConcurrentModificationException 异常。
理解这个函数的前提是分清楚size和length的不同之处:
size是ArrayList实际包含的成员个数;而length是ArrayList的容量,即最多容纳多少元素。
比如ArrayList的length可以为10,但是只包含6个成员,其余为null。
trimToSize()方法的目的就是使length等于size,以节约存储空间。
当size小于elementData的length时:
如果size为0,说明 elementData 不包含任何成员,把elementData指向 EMPTY_ELEMENTDATA 即可;
如果size不为0,说明elementData包含成员,这时调用copyOf(),做一次copy,使ArrayList的容量大小和size相等(这里主要目的是理解ArrayList,暂时不对copyOf()进行说明,后续文章详细介绍)。
下篇文章我们主要分析ArrayList的扩容机制。
ArrayList源码解析(一)的更多相关文章
- ArrayList源码解析
ArrayList简介 ArrayList定义 1 public class ArrayList<E> extends AbstractList<E> implements L ...
- 顺序线性表 ---- ArrayList 源码解析及实现原理分析
原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7738888.html ------------------------------------ ...
- 面试必备:ArrayList源码解析(JDK8)
面试必备:ArrayList源码解析(JDK8) https://blog.csdn.net/zxt0601/article/details/77281231 概述很久没有写博客了,准确的说17年以来 ...
- ArrayList源码解析(二)
欢迎转载,转载烦请注明出处,谢谢. https://www.cnblogs.com/sx-wuyj/p/11177257.html 自己学习ArrayList源码的一些心得记录. 继续上一篇,Arra ...
- Java中的容器(集合)之ArrayList源码解析
1.ArrayList源码解析 源码解析: 如下源码来自JDK8(如需查看ArrayList扩容源码解析请跳转至<Java中的容器(集合)>第十条):. package java.util ...
- Collection集合重难点梳理,增强for注意事项和三种遍历的应用场景,栈和队列特点,数组和链表特点,ArrayList源码解析, LinkedList-源码解析
重难点梳理 使用到的新单词: 1.collection[kəˈlekʃn] 聚集 2.empty[ˈempti] 空的 3.clear[klɪə(r)] 清除 4.iterator 迭代器 学习目标: ...
- ArrayList源码解析--值得深读
ArrayList源码解析 基于jdk1.8 ArrayList的定义 类注释 允许put null值,会自动扩容: size isEmpty.get.set.add等方法时间复杂度是O(1): 是非 ...
- 【源码解析】- ArrayList源码解析,绝对详细
ArrayList源码解析 简介 ArrayList是Java集合框架中非常常用的一种数据结构.继承自AbstractList,实现了List接口.底层基于数组来实现动态容量大小的控制,允许null值 ...
- Java集合-ArrayList源码解析-JDK1.8
◆ ArrayList简介 ◆ ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAcc ...
随机推荐
- 安全性测试之防范 DDoS 攻击
安全性测试之防范 DDoS 攻击 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:90882 ...
- C++学习笔记之模板篇
title: C++学习笔记之模板篇 tags: c++,c,模板,vector,friend,static,运算符重载,标准模板 --- 一.模板 不管是函数模板还是类模板,在未初始化前都是不占用内 ...
- jQuery中append(),prepend()与after(),before()的区别
在jQuery中,添加元素有append(),prepend和 after(),before()两种共四个. 根据字面意思我们可以看出他们分别是追加,添加和之前,之后,意思相近.同时他们又都有添加元素 ...
- Mac OS平台下应用程序安装包制作工具Packages的使用介绍(补充)
上一篇:Mac OS平台下应用程序安装包制作工具Packages的使用介绍 补充说明 上一篇文章中介绍了如何使用Packages如何创建mac下的安装包.但是这样制作出来的安装包只能安装到系统的文件路 ...
- iOS 关于定位你该注意的那些事
其实现在对于一个APP来说,定位用户的位置是件很容易的事情,有三种解决方案供您选择: (1)原生 (2)高德地图 (3)百度地图 1.解决方案的选择 其实单说iOS开发来说应用哪种方案都无所谓 ...
- 关于Java内存管理的几个小技巧
这里将介绍几则Java内存管理的小技巧,让你让你从Java入门开始告别陋习,为Java程序提速.有不少人都说"Java完了,只等着衰亡吧!",为什么呢?最简单的的例子就是Java做 ...
- iOS·官方文档译文框架源码注解
导语
- Web API 之SelfHost与OwinSelfHots加载外部程序
下面就一些web api的一些基础内容进行阐述,然后就web api宿主承载中的实际业务问题进行解决 HttpController HttpController的激活是由处于消息处理管 ...
- Linux基础测试--11道题
000.创建一个目录/data mkdir /data 001.在/data 下面创建一个文件oldboy.txt touch /data/oldboy.txt 002.为oldboy.txt 增加内 ...
- bzoj3064 Tyvj 1518 CPU监控
Description Bob需要一个程序来监视CPU使用率.这是一个很繁琐的过程,为了让问题更加简单,Bob会慢慢列出今天会在用计算机时做什么事. Bob会干很多事,除了跑暴力程序看视频之外,还会做 ...