一、类继承关系

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

ArrayList继承AbstractList,也实现了List和RandomAccess(一个空接口,也就是标记接口。),Cloneable(可被克隆), Serializable接口。

二、类属性

    //默认容量
private static final int DEFAULT_CAPACITY = 10; //空对象数组(initialCapacity == 0时返回)
private static final Object[] EMPTY_ELEMENTDATA = {}; //调用无参构造方法,返回的是该数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //元素数组,保存添加到ArrayList中的元素
transient Object[] elementData; //ArrayList大小
private int size;

elementData是transient修饰,所以elementData不能被序列化。但是ArrayList又是可以被序列化的,这是为何?

  • 因为ArrayList有两个方法writeObject、readObject用于序列化和反序列化。elementData是transient修饰是为了避免ArrayList扩容(1.5倍)导致的空间浪费
  1. 序列化的时候elementData的数据会被写到ObjectOutputStream中
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size); // Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
} if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
  1. 反序列化的时候会从ObjectInputStream读取出来
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff
s.defaultReadObject(); // Read in capacity
s.readInt(); // ignored if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size); Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}

三、构造函数

  • 指定容量
    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);
}
}
  • 默认
    public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • ArrayList(Collection<? extends E> c)
    public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//空对象数组
this.elementData = EMPTY_ELEMENTDATA;
}
}

四、ArrayList如何扩容

    public boolean add(E e) {
//确保elementData数组的大小
ensureCapacityInternal(size + 1);
//在结尾处添加元素
elementData[size++] = e;
return true;
}

上面是add(E e)方法的源码,在添加元素前会调用 ensureCapacityInternal(size + 1);确保elementData数组的大小。

    private void ensureCapacityInternal(int minCapacity) {
// 判断元素数组是否为空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//minCapacity最少=DEFAULT_CAPACITY=10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
}
  • 其中ensureCapacityInternal的源码片段首先判断elementData是否为无参构造器的空数组,如果是的话,Math.max()取较大值赋值给minCapacity,最小是10(默认容量)。然后,调用ensureExplicitCapacity:
    private void ensureExplicitCapacity(int minCapacity) {
// List结构性修改加1
modCount++;
//minCapacity大于elementData数组长度
if (minCapacity - elementData.length > 0)
//增长
grow(minCapacity);
}
  • ensureExplicitCapacity中modCount记录List结构性变化次数(结构性变化,在迭代的过程中可能会造成错误结果)。
  • 紧接着,如果minCapacity大于elementData数组长度则调用 grow(minCapacity)增长函数

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//oldCapacity >> 1右移一位,这里相当于扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}

上面的扩容方法中 oldCapacity >> 1 表明elementData.length右移一位,扩容后的容量为原始容量的1.5倍

五、主要函数

remove()函数
    public E remove(int index) {
rangeCheck(index); modCount++;
E oldValue = elementData(index);
//需要移动的元素个数
int numMoved = size - index - 1;
//删除的不是最后一个元素
if (numMoved > 0)
//index后的元素都向前移动一位
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//size减一,释放引用,方便垃圾回收器进行垃圾回收
elementData[--size] = null; // clear to let GC do its work return oldValue;
}
  • remove(int index)方法会先计算需要移动的元素个数,index后的元素都向前移动一位(调用System.arraycopy方法移动),然后size减一,赋值为null释放引用,方便垃圾回收器进行垃圾回收。
单线程环境下,正确的删除元素,应该使用iterator()迭代器删除元素。

例如要删除集合中的“a”

        Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
if ("a".equals(iterator.next())) {
iterator.remove();
}
}

或者使用lambda表达式

list.removeIf("x"::equals);
set()函数,会替换新值,返回旧值。
    public E set(int index, E element) {
//检查索引
rangeCheck(index); E oldValue = elementData(index);
elementData[index] = element;
//返回旧值
return oldValue;
}
get()函数,获取元素
    public E get(int index) {
rangeCheck(index); return elementData(index);
}

六、ArrayList性能相关

  • 在能够确定ArrayList容量大小的情况下尽量预估容量,调用构造器public ArrayList(int initialCapacity)指定大小,避免因为自动扩容带来的性能开销。
  • 使用trimToSize()使ArrayList的内部数组大小减少为实际大小,以此节省空间
    public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}

【Java源码】集合类-ArrayList的更多相关文章

  1. Java源码之ArrayList

    本文源码均来自Java 8 总体介绍 Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类.Set和List两个类继承于它.Set中不能包含重复的元素,也没有顺序来存放. ...

  2. 浅析Java源码之ArrayList

    面试题经常会问到LinkedList与ArrayList的区别,与其背网上的废话,不如直接撸源码! 文章源码来源于JRE1.8,java.util.ArrayList 既然是浅析,就主要针对该数据结构 ...

  3. Java源码阅读ArrayList

    1简介 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAc ...

  4. Java源码之ArrayList分析

    一.ArrayList简介 ArrayList底层的数据结构是数组,数组元素类型为Object类型,即可以存放所有类型数据. 与Java中的数组相比,它的容量能动态增长.当创建一个数组的时候,就必须确 ...

  5. Java源码-集合-ArrayList

    基于JDK1.8.0_191 介绍   在Java中,对于数据的保存和使用有多种方式,主要的目的是以更少的资源消耗解决更多的问题,数组就是其中的一种,它的特点是所有的数据都保存在内存的一段连续空间中, ...

  6. Java源码解析——集合框架(一)——ArrayList

    ArrayList源码分析 ArrayList就是动态数组,是Array的复杂版本,它提供了动态的增加和减少元素.灵活的设置数组的大小. 一.类声明 public class ArrayList< ...

  7. Java源码系列1——ArrayList

    本文简单介绍了 ArrayList,并对扩容,添加,删除操作的源代码做分析.能力有限,欢迎指正. ArrayList是什么? ArrayList 就是数组列表,主要用来装载数据.底层实现是数组 Obj ...

  8. Java集合源码剖析——ArrayList源码剖析

    ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...

  9. JDK1.8源码学习-ArrayList

    JDK1.8源码学习-ArrayList 目录 一.ArrayList简介 为了弥补普通数组无法自动扩容的不足,Java提供了集合类,其中ArrayList对数组进行了封装,使其可以自动的扩容或缩小长 ...

  10. Jdk源码-集合类主要原理和解析

    写在前面 熟悉Jdk原理的重要性不言而喻,作为Java开发者或者面试者,了解其实现原理也显得更为装逼,在Java读书计划我写到了,它是面试中最基础的一部分,所以单独拿出来做个总结,为了更好滴理解和学习 ...

随机推荐

  1. Win10 系统安装Sql Server2008 R2 数据库遇到的问题及解决办法总结!

    1.开始安装时,提示要先安装 “.NET Framework 3.5(包括.NET 2.0和3.0)”,之前已经下载好.NET Framework 3.5 sp1,安装时还是提示要先安装 “.NET  ...

  2. 第17周翻译:SQL Server中的事务日志管理的阶梯:第5级:在完全恢复模式下管理日志

    来源:http://www.sqlservercentral.com/articles/Stairway+Series/73785/ 作者:Tony Davis, 2012/01/27 翻译:刘琼滨. ...

  3. MFC (Combo-box control)下拉列表控件的使用

    1.可以选择,但无法编辑状态: 选择下拉框的属性中的类型(Type)参数——Drop List; 2.如何控制Combo Box的下拉长度 1,一.在资源文件中打开对话框,选中Combo Box控件, ...

  4. MySQL-07 日志管理

    学习目标 MySQL日志 二进制日志 错误日志 查询通用日志 慢查询日志 MySQL日志 MySQL日志分为四类,说明如下: 错误日志:记录MySQL服务的启动.运行或者停止时出现的问题. 查询日志: ...

  5. docker guide

    centos docker community version install: yum -y install docker # install docker systemctl start dock ...

  6. node.js从入门到放弃(一)

    以下内容全是我个人理解写出,如有不对,请立刻练习本人进行更改.以免被刚入门的被我带入坑里. —node是什么?我想大家应该都知道. node是前端未来干掉后端的一种语言,是用JavaScript来编写 ...

  7. redhat 7.x 、redhat 6.x查看硬盘UUID方法

    1.查看磁盘分区UUID: [root@rac01 ~]# blkid /dev/sdb1: UUID="6bba92c4-0b25-4cc4-9442-ca87c563720a" ...

  8. centos7下安装Python3.7(与python2共存)

    2019-01-22   22:50:05 centos7默认安装的是python2.7,然而python2基本上要淘汰了,所以有必要安装最新的python3 python,g++这些工具一般安装在/ ...

  9. LeetCoce 413. Arithmetic Slices

    A sequence of number is called arithmetic if it consists of at least three elements and if the diffe ...

  10. 线性结构4 Pop Sequence

    02-线性结构4 Pop Sequence(25 分) Given a stack which can keep M numbers at most. Push N numbers in the or ...