ArrayList的源码分析
在项目中经常会用到list集合来存储数据,而其中ArrayList是用的最多的的一个集合,这篇博文主要简单介绍ArrayList的源码分析,基于JDK1.7:
这里主要介绍 集合 的属性,构造器,和方法:方法主要基于 add(E e),get(int index),remove(int index),set(int index,E e);
其中,E指的是泛型;
1:属性:
- /**
- * Default initial capacity.
- */
- private static final int DEFAULT_CAPACITY = 10;
- /**
- * Shared empty array instance used for empty instances.
- */
- private static final Object[] 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 == EMPTY_ELEMENTDATA will be expanded to
- * DEFAULT_CAPACITY when the first element is added.
- */
- private transient Object[] elementData;
- /**
- * The size of the ArrayList (the number of elements it contains).
- *
- * @serial
- */
- private int size;
首先对几个修饰符进行说明一下:
final:修饰常量,表示不可变常量;
transient :修饰的字段,不会被序列化;
从 private transient Object[] elementData; 这个字段中可以看出ArrayList底层维护的是一个数组,该数组可以存放任何的Object类型;
DEFAULT_CAPACITY :说明集合的默认长度是10;但是在后续的源码跟踪中没有发现 集合的默认长都为10 的这一体现,希望有大神能够告知;
EMPTY_ELEMENTDATA = {} 不可变空数组,用于方法中的共享;
size:集合的包含元素的长度;
2:构造器:
- /**
- * 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) {
- super();
- if (initialCapacity < 0)
- throw new IllegalArgumentException("Illegal Capacity: "+
- initialCapacity);
- this.elementData = new Object[initialCapacity];
- }
- /**
- * Constructs an empty list with an initial capacity of ten.
- */
- public ArrayList() {
- super();
- this.elementData = EMPTY_ELEMENTDATA;
- }
- /**
- * Constructs a list containing the elements of the specified
- * collection, in the order they are returned by the collection's
- * iterator.
- *
- * @param c the collection whose elements are to be placed into this list
- * @throws NullPointerException if the specified collection is null
- */
- public ArrayList(Collection<? extends E> c) {
- elementData = c.toArray();
- size = elementData.length;
- // c.toArray might (incorrectly) not return Object[] (see 6260652)
- if (elementData.getClass() != Object[].class)
- elementData = Arrays.copyOf(elementData, size, Object[].class);
- }
比较常用的有
public ArrayList(int initialCapacity) 和 public ArrayList();所以这里只都这两个构造器进行分析;
public ArrayList(int initialCapacity);构造时指定集合的初始化大小;将构建好的集合赋给属性:
this.elementData=new Object[initialCapacity];以便在其他的方法中可以用这个集合;如果initialCapacity<0则会抛出参数非法的异常;
public ArrayList();其中:this.elementData = EMPTY_ELEMENTDATA;构建一个空集合,当然注释不是特别明白: Constructs an empty list with an initial capacity of ten:初始化容量为10的空集合
3:方法:
add(E e);以空集合和集合长度为10为例:
1):空集合:int size=0;
调用的方法: ensureCapacityInternal(size + 1);参数为 1;
方法的源码:
- private void ensureCapacityInternal(int minCapacity) { // minCapacity=1
- if (elementData == EMPTY_ELEMENTDATA) { true
- minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //minCapacity=10
- }
- ensureExplicitCapacity(minCapacity); //minCapacity=10
- }
- 接下来调用这个方法:ensureExplicitCapacity(minCapacity) // minCapacity=10
- private void ensureExplicitCapacity(int minCapacity) { //minCapacity=10
- modCount++; //记录构造器调用次数
- // overflow-conscious code
- if (minCapacity - elementData.length > 0) //elementData.length=0
- grow(minCapacity);
- }
接下来调用 grow(minCapacity); //minCapacity=10
源码:
- private void grow(int minCapacity) { //minCapacity=10
- // overflow-conscious code
- int oldCapacity = elementData.length; //oldCapacity=0
- int newCapacity = oldCapacity + (oldCapacity >> 1); //newCapacity=0
- if (newCapacity - minCapacity < 0) // newCapacity - minCapacity=-10
- newCapacity = minCapacity; // newCapacity=10
- if (newCapacity - MAX_ARRAY_SIZE > 0) //MAX_ARRAY_SIZE=2147483639
- newCapacity = hugeCapacity(minCapacity);
- // minCapacity is usually close to size, so this is a win:
- elementData = Arrays.copyOf(elementData, newCapacity); // elementData[]={},newCapacity=10
- }
- public static <T> T[] copyOf(T[] original, int newLength) {
- return (T[]) copyOf(original, newLength, original.getClass());
- }
接下来分析 copyOf 的源码;
- public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { // original={},newLength=10,
- T[] copy = ((Object)newType == (Object)Object[].class) //如果 newType 为 Object[]类型返回Object[10] 如果不是则构造Object[10]返回
- ? (T[]) new Object[newLength]
- : (T[]) Array.newInstance(newType.getComponentType(), newLength);
- System.arraycopy(original, 0, copy, 0,
- Math.min(original.length, newLength));
- return copy;
- }
- System.arraycopy(original, 0, copy, 0, 10); // original={} copy=Object[10]
- 源码到这里就不能再继续跟踪下去了 arraycopy() 方法是被native修饰的,是以c写成的语言;
根据注释中可以知道方法的作用:- original:源数组 0:原数组的其实位置 copy:目标数组 0:目标数组的起始位置 10:需要拷贝的数组个数
将源数组拷贝到目标数组中,此时:集合中的数组为 Object[10];
接下来:
elementData[size++] = e; //size++=1
- elementData[1]=e;
2):集合的原长度是10,此时需要增加一个元素:
- public boolean add(E e) {
- ensureCapacityInternal(size + 1); // Increments modCount!! size=10 size+1=11
- elementData[size++] = e;
- return true;
- }
- private void ensureCapacityInternal(int minCapacity) { //minCapacity=11
- if (elementData == EMPTY_ELEMENTDATA) { //false
- minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
- }
- ensureExplicitCapacity(minCapacity);
- }
- private void ensureExplicitCapacity(int minCapacity) { // minCapacity=11
- modCount++;
- // overflow-conscious code
- if (minCapacity - elementData.length > 0) //true 当数组中的元素个数等于数组自定长度时则返回true 因为此时 minCapacity=elementData.length+1 否则返回 false
- grow(minCapacity);
- }
- private void grow(int minCapacity) { //minCapacity=11
- // overflow-conscious code
- int oldCapacity = elementData.length; //oldCapacity=10
- int newCapacity = oldCapacity + (oldCapacity >> 1); //newCapacity=15
- if (newCapacity - minCapacity < 0)
- newCapacity = minCapacity;
- if (newCapacity - MAX_ARRAY_SIZE > 0)
- newCapacity = hugeCapacity(minCapacity);
- // minCapacity is usually close to size, so this is a win:
- elementData = Arrays.copyOf(elementData, newCapacity);
- }
- Arrays.copyOf(elementData, newCapacity);内部的实现方法:新建一个长度为15的数组,将旧数组的内容拷贝到新的数组中;拷贝到新数组中从0开始作为起始位;
到此 add(E e)方法的源码就分析完了;- 接下来分析get(int index)的源码:
- public E get(int index) {
- rangeCheck(index); //检查是否数组越界了
- return elementData(index); //返回指定的索引的元素
- }
- private void rangeCheck(int index) {
- if (index >= size)
- throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
- }
接下来分析remove(int index) 的方法:
- 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); //将源数组从index+1的元素移到目标数组从index为开始的位置,移动的元素个数为 numMoved
- elementData[--size] = null; // clear to let GC do its work
- return oldValue;
- }
接下来是set(int index,E e)
- public E set(int index, E element) {
- rangeCheck(index); //检查是否数组越界
- E oldValue = elementData(index); //获得原先的元素
- elementData[index] = element; //将element元素赋值给索引是index的元素
- return oldValue;
- }
ok了;
mark:调用集合的add(E e)方法时,当需要添加的元素个数超出数组的空余长度时则需要扩充数组的长度,每次扩充的长度是原来数组的1.5倍(jdk1.6之前的版本则是原来数组的1.5倍+1),扩充后的数组是构创建一个新数组,原先的数组则会被回收;让后将原先就数组的元素拷贝到新数组中,这样递归地扩充拷贝很影响集合的运行效率,所以当知道集合元素个数时,在出事后集合大小的时候指定集合的长度,这样开销会小很多;否则则可以考虑用linkedList来存储数据;
查询是基于索引查询的,用ArrayList效率会很快
ArrayList的源码分析的更多相关文章
- ArrayList迭代器源码分析
集合的遍历 Java集合框架中容器有很多种类,如下图中: 对于有索引的List集合可以通过for循环遍历集合: List<String> list = new ArrayList<& ...
- ArrayList实现源码分析
本文将以以下几个问题来探讨ArrayList的源码实现 1.ArrayList的大小是如何自动增加的 2.什么情况下你会使用ArrayList?什么时候你会选择LinkedList? 3.如何复制某个 ...
- 数据结构——ArrayList的源码分析(你所有的疑问,都会被解答)
一.首先来看一下ArrayList的类图: 1,实现了RandomAccess接口,可以达到随机访问的效果. 2,实现了Serializable接口,可以用来序列化或者反序列化. 3,实现了List接 ...
- ArrayList方法源码分析
本文将从ArrayList类的存储结构.初始化.增删数据.扩容处理以及元素迭代等几个方面,分析该类常用方法的源码. 数据存储设计 该类用一个Object类型的数组存储容器的元素.对于容量为空的情况,提 ...
- 集合之ArrayList的源码分析
转载请注明出处 一.介绍 对于ArrayList,可以说诸位绝不陌生,可以说是在诸多集合中运用的最多一个类之一,那么它是怎样构成,怎样实现的呢,相信很多人都知道数组构成的,没毛病,如果遇到面试的时候, ...
- Java——ArrayList底层源码分析
1.简介 ArrayList 是最常用的 List 实现类,内部是通过数组实现的,它允许对元素进行快速随机访问.数组的缺点是每个元素之间不能有间隔, 当数组大小不满足时需要增加存储能力,就要将已经有数 ...
- 迎难而上ArrayList,源码分析走一波
先看再点赞,给自己一点思考的时间,思考过后请毫不犹豫微信搜索[沉默王二],关注这个长发飘飘却靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有技术大佬整理 ...
- ArrayList<E>源码分析
ArrayList是按照线性表结构实现的 ArrayList的主要继承结构 public class ArrayList<E> extends AbstractList<E> ...
- ArrayList构造方法源码分析
首先看一下无参的构造方法: private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object ...
随机推荐
- Visual Studio 2015部署OpenCV
Visual Studio 2015的安装破解及OpenCV的安装和部署 平台:win10 X64+VS2015专业版 X64:vs2015.pro_chs+OpenCV 3.X:opencv-3.4 ...
- 2018.12.22 bzoj3926: [Zjoi2015]诸神眷顾的幻想乡(广义后缀自动机)
传送门 题意简述:给出一棵trietrietrie树,每个点表示一个字符,求树上所有路径组成的不同字串数.(叶子数≤20\le 20≤20) 由于有一个神奇的条件,考虑以每一个叶子为树根统计每个点到树 ...
- 2018.06.26 NOIP模拟 纪念碑(线段树+扫描线)
题解: 题目背景 SOURCE:NOIP2015−GDZSJNZXSOURCE:NOIP2015-GDZSJNZXSOURCE:NOIP2015−GDZSJNZX(难) 题目描述 2034203420 ...
- PHP中奖概率实现
$prize_arr = array( '0' => array('id'=>1,'prize'=>'elm_1','rate'=>1), '1' => array('i ...
- ThinkPHP3.2 伪静态配置
前台伪静态且隐藏入口文件 就把“.htaccess文件” 放到指定文件夹下面 如图home做伪静态并隐藏入口文件: Apache为例,需要在入口文件的同级添加.htaccess文件 如果用的phpst ...
- jitter
release jitter of tasks there is a distinction between the real activation request and the actual ac ...
- 安装docker跨主机网络flannel
一.实验环境 机器 操作系统 安装服务 172.16.4.36 centos7 docker etcd flannel 172.16.4.37 centos7 docker etcd flanne ( ...
- adb 相关命令 以及无法adb识别设备的解决方法
[自己解决方法] 在-/.android/文件夹下面新建adb_usb.ini文件.里面写入设备的idVendor号(0x加上四位数字),然后输入 adb kill-server, 然后adb dev ...
- 20145232 韩文浩 《Java程序设计》第3周学习总结
教材学习内容总结 在第三章中,知道了Java可区分为基本类型和类类型两大类型系统,其中类类型也称为参考类型.在这一周主要学习了类类型. 对象(Object):存在的具体实体,具有明确的状态和行为 类( ...
- MapGIS10.3新功能
智能的GIS 支持开放的数据集.数据库.等等 T-C-V 软件结构是继局部网软件的 C/S 结构,互联网软件的 B/S 结构发展起来的适合云 计算.云服务的新一代软件三层结构,分别为终端应用层(T 层 ...