ArrayList在平常用的还挺多的,用起来十分舒服,顺手。这里来学习一下它的源码。

类定义

下面是类的定义:

 public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable { }

实现的接口还挺多的。

类属性

首先来看下一ArrayList的几个属性:

1. DEFAULT_CAPACITY:初始容量

 private static final int DEFAULT_CAPACITY = 10;

DEFAULT_CAPACITY,是ArrayList的初始容量,有意思是它使用的时机。

2. EMPTY_ELEMENTDATA:共享的空数组

 private static final Object[] EMPTY_ELEMENTDATA = {};

英文注释是:"Shared empty array instance used for empty instances."。这个看上去还是很好理解的,但是下面还有一个属性也是空数组。

3. DEFAULTCAPACITY_EMPTY_ELEMENTDATA:默认容量的空数组

好奇怪,用一个EMPTY_ELEMENTDATA不就可以了么,为什么还要用一个DEFAULTCAPACITY_EMPTY_ELEMENTDATA呢?当我们使用new ArrayList()的时候就会发现这个空数组实例排上用场了:

 public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

这个方法的英文注释是:"Constructs an empty list with an initial capacity of ten."。但是这里其实就把这个空的数组对象DEFAULTCAPACITY_EMPTY_ELEMENTDATA给了elementData,并没有实例化额外的对象数组。

4. elementData:实际元素所在的数组

 transient Object[] elementData; // non-private to simplify nested class access

elementData才是元素真正存放的地方,这里不明白的是为什么要用transient关键字。

这个数组缓冲区是ArrayList存储元素的地方。ArrayList的容量正是这个数组缓冲区的长度。任何elementData为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList会在添加第一个元素的时候,将容量扩充到DEFAULT_CAPACITY(目前是10)。

5. size:数组中元素的长度

 private int size;

这里的size并不是elementData数组的长度,而是这个数组真正包含的元素的个数(这个个数其实有待商榷,后面会讲到)。

构造方法

在ArrayList中一共有3个构造方法:

 // 给定初始容量
public ArrayList(int initialCapacity); // 空参构造方法
public ArrayList(); // 从其他集合类中构造
public ArrayList(Collection<? extends E> c);

1. 给定初始容量的constructor

 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 > 0为真时,创建一个大小为initialCapacity的空数组,并将引用赋给elementData
  • initialCapacity == 0为真时,把之前的空数组EMPTY_ELEMENTDATAelementData
  • initialCapacity < 0为真时,直接抛出IllegalArgumentException异常。

唯一一点是为啥这个时候要用EMPTY_ELEMENTDATA了,而不是继续用原来的DEFAULTCAPACITY_EMPTY_ELEMENTDATA了。

2. 空参构造方法

 public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

这个就比较简单了,和上面对比的是,这个时候为啥要用DEFAULTCAPACITY_EMPTY_ELEMENTDATA

问题来了:new ArrayList(0)和new ArrayList()有啥不同呢?

唯一的不同就在于内部数组用的不是同一个,虽然它们都是空的。

3. 从集合类中构造

 public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); // 注意, 这个时候ArrayList中元素的顺序取决于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;
}
}

这里有两个地方需要注意下:

  • 集合转ArrayList之后元素的顺序是由集合的toArray方法所决定的;
  • toArray可能会不正确地返回数组对象Object[].class, see 6260652

Collection接口的toArray方法依赖于实现类中的具体实现,有时候ArrayList中的顺序对我们来说还是挺重要的。然后在来看一下,这个官方bug,我在网上查了一下,发现这篇写的不错:https://blog.csdn.net/gulu_gulu_jp/article/details/51457492

新增元素的方法

新增元素的方法是ArrayList中最值得关注的,它提供了两种add方法:

  • 没有index的add方法,即在内部数组的末尾添加一个元素;
  • 在给定的index处插入元素,index右边的元素会后移。

没有index的add方法

 public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

ensureCapacityInternal方法在ArrayList中很多地方都用到了,非常关键的方法。

当插入新元素所需的最小capacity要比内部数组的长度大的时候,就要扩容了,现在原来的基础之上扩容50%,如果新的capacity比最小的capacity还要小的话,直接用所需最小的capacity作为capacity。

有index的方法

在这种情况下是在ArrayList的某个位置上插入一个元素,然后把index后边的元素集体往右移一位。需要注意的问题是,index的范围是有限制的,必须在[0, size)之间,那么这中检查是为了不让ArrayList之间出现空洞。

 public void add(int index, E element) {
rangeCheckForAdd(index); // 返回检查
ensureCapacityInternal(size + 1); // 扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index); // 右移元素
elementData[index] = element; // 插入新元素
size++;
}

ArrayList中的内部模型

理解了ArrayList的内部模型的话,其实看不看源码感觉关系不大。

其他方法

其他方法我觉得还挺常规的,没有太多好说的,就是System.arraycopy用得还挺多的,专门用于复制数组。


以上~

Java API学习(一) ArrayList源码学习的更多相关文章

  1. java集合系列之ArrayList源码分析

    java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...

  2. 数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解

    数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解 对数组有不了解的可以先看看我的另一篇文章,那篇文章对数组有很多详细的解析,而本篇文章则着重讲动态数组,另一篇文章链接 ...

  3. ArrayList源码学习----JDK1.7

    什么是ArrayList? ArrayList是存储一组数据的集合,底层也是基于数组的方式实现,实际上也是对数组元素的增删改查:它的主要特点是: 有序:(基于数组实现) 随机访问速度快:(进行随机访问 ...

  4. 集合类学习之ArrayList源码解析

    1.概述 ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大 ...

  5. ArrayList源码学习

    1.ArrayList:基于数据实现,允许出现空值和重复元素,当ArrayList中添加的元素数量大于底层数组容量是,会通过扩容机制重新生成一个更大的数组.(非线程安全) 2.源码分析 构造函数 /* ...

  6. Concurrent包学习之 BlockingQueue源码学习

    上一篇学习了ExecutorService和其它相关类的源码,本篇要学习的BlockingQueue中的源码,as always,先上类图 其实继承(实现)的层次比较简单,我们只要需要先学习一下Blo ...

  7. Java基础-集合框架-ArrayList源码分析

    一.JDK中ArrayList是如何实现的 1.先看下ArrayList从上而下的层次图: 说明: 从图中可以看出,ArrayList只是最下层的实现类,集合的规则和扩展都是AbstractList. ...

  8. 【java集合总结】-- ArrayList源码解析

    一.前言 要想深入的了解集合就必须要通过分析源码来了解它,那如何来看源码,要看什么东西呢?主要从三个方面: 1.看继承结构 看这个类的继承结构,处于一个什么位置,不需要背记,有个大概的感觉就可以,我自 ...

  9. Java集合系列[1]----ArrayList源码分析

    本篇分析ArrayList的源码,在分析之前先跟大家谈一谈数组.数组可能是我们最早接触到的数据结构之一,它是在内存中划分出一块连续的地址空间用来进行元素的存储,由于它直接操作内存,所以数组的性能要比集 ...

随机推荐

  1. 如何调试makefile变量

    六.七年前写过一篇<跟我一起写Makefile>,直到今天,还有一些朋友问我一些Makefile的问题,老实说,我有一段时间没有用Makefile了,生疏了.回顾,这几年来大家问题我的问题 ...

  2. iOS UILabel设置居上对齐,居中对齐,居下对齐

    在iOS中默认的UILabel中的文字在竖直方向上仅仅能居中对齐,博主參考国外站点.从UILabel继承了一个新类,实现了居上对齐,居中对齐,居下对齐.详细例如以下: // //  myUILabel ...

  3. Java 内存溢出思维导图

    文 by / 林本托 Tips 做一个终身学习的人. 在 Java 内存中,只有一个区域不会发生 OOM 异常,那就是程序计数器内存.下面的思维导图记录了每个内存区域发生内存异常的条件和基本的解决思路 ...

  4. highlightjs 详解

    起源: 最近想做一个代码高亮的功能.发现开源社区已经有了这类的项目.比如说highlightjs. 第一步:下载highlightjs 官网:https://highlightjs.org 可以看到它 ...

  5. PHP实现无符号右移(js中的 >>>)

    移位包括有符号左移(<<).有符号右移(>>).无符号右移(>>>),其中 js 支持三种移位,PHP只支持前两种移位(没查到第三种),恰好需要PHP进行无符 ...

  6. Ucloud的自主研发的检测主机是否被入侵的agent

    wget --timeout 3 -t 2 http://download.uhostsec.service.ucloud.cn:8090/ucloud-secagent-install.sh -O ...

  7. 解决 SQL Server 所有帐号无 sysadmin 权限,且未启用 SQL Server 身份验证,sa 帐号也未启用的问题

    解决 未启用 SQL Server 身份验证 的问题: 1. 运行 regedit,进入注册表编辑器 2. 打开:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Micro ...

  8. jenkins + gerrit 自动code review

    最近有需求要push review以后自动跑一些测试,如果通过就自动+2 不通过就-2,目前做法如下(jenkins gerrit均已配置好,Jenkins可以连接gerrit并拉代码): 1. Je ...

  9. C++编程学习50个经典网站 强力推荐(转载)

    转自:http://blog.csdn.net/microzone/article/details/6684436 C/C++是最主要的编程语言.这里列出了50名优秀网站和网页清单,这些网站提供c/c ...

  10. rpm 安装zabbix 注意

    # grep zabbix /etc/sudoers zabbix ALL=(ALL) NOPASSWD: /sbin/blockdev