一、类继承关系

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. COGS 1439. [NOIP2013]货车运输

    ★★☆   输入文件:truck.in   输出文件:truck.out   简单对比时间限制:1 s   内存限制:128 MB [题目描述] [来源] CCF全国信息学奥林匹克联赛(NOIP201 ...

  2. 洛谷 P1434 滑雪

    题目描述 Michael喜欢滑雪.这并不奇怪,因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道在一个区域中最长 ...

  3. javaee 第四周作业

    分析hello.java.下载链接:https://github.com/javaee/tutorial-examples/tree/master/web/jsf/hello1 /** * Copyr ...

  4. iOS 如何使用TabbarController

    xcode中给我内置很多app模版,不过很多时候我们需要更加灵活的初始化项目.下面我就简单介绍一下,如何从0开始制作一个tabbar app. 创建个项目,由于我们从头开始写程序,因此理论上对模版没有 ...

  5. python基础一 day5 集合

    集合是无序的 增:add()添加进去是无序,不一定是最后面,update()像extend() 删: 没有改,有查,里面的元素是不可变类型 查用for in 交集: 并集: 反交集 叉集: 子集与超集 ...

  6. 路由器wan口ip地址显示0.0.0.0怎么办

    http://m.xuexila.com/luyouqi/671049.html 这个网络时代里面我们最常用来连接网络的设备就是路由器了,现在的社会不管是工作还是生活几乎都离不开网络了,同时我们也要学 ...

  7. android 图片

    1,setCompoundDrawables(Drawable left, Drawable top,Drawable right, Drawable bottom) 设置图片出现在textView, ...

  8. noip2019——动态规划刷题历程

    加粗的是值得总结的 从洛谷的普及题开始刷题: 背包式dp(有些技巧的) 1.p2639[USACO09OCT]Bessie的体重问题 -p1049取模意义下01背包 技巧:重量=价值 2.金明的预算问 ...

  9. php微信公众号开发之快递查询

    [文章来源:脚本之家   文章地址:https://www.jb51.net/article/149205.htm] 本文实例为大家分享了php微信公众号开发之快递查询的具体代码,供大家参考,具体内容 ...

  10. docker 私有镜像服务器搭建

    1.准备一台服务器A(已安装docker, IP:192.168.39.111) 2.在服务器A上通过运行registry容器进行搭建 docker run -itd -v /my_registry: ...