java集合核心源码01——ArrayList
Posted 自带锋芒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java集合核心源码01——ArrayList相关的知识,希望对你有一定的参考价值。
首先呢,对于ArrayList,相当于一个动态数组,里面可以存储重复数据,并且支持随机访问,不是线程安全的。对于更多的底层东西,且听分解。
打开源码,先看继承了哪些类,实现了哪些接口,然后继承的这些类或接口是否还有父类,一直深挖到顶部
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
可以看出,ArrayList集合继承了AbstractList类,实现了List,RandomAccess,Cloneable,Serializable这一系列接口,一个一个分析。
其中呢AbstractList类最终也是实现了Collection接口,List也是继承了Collection接口,同时呢Collection接口继承了Iterable接口。
对于Iterable接口呢,源码中解释说,for-each循环可以与任何实现了Iterable接口的对象一起工作,就是说该集合可以使用for-each循环。
对于Cloneable,Serializable这两个接口,代表该类支持克隆和序列化。
对于RandomAccess,实现了该接口,那么该类就支持快速的随机访问。LinkedList就没有实现该接口,需要用迭代器进行遍历。
对于AbstractList类,里面有一个属性modCount,这个属性主要是由集合的迭代器使用的,在迭代的过程中,会检查当前的List的modCount和自己保存的比较,是否发生了变化(变化是指该集合是否被其他线程并发修改),如果发生变化,就抛出java.util.ConcurrentModificationException异常,这种行为就是fail-fast机制
接下来,我们真正的走进ArrayList源码:
transient Object[] elementData;
首先,ArrayList底层也是封装的一个数组,这个数组是被transient关键字修饰的,代表对象的临时数据,持久化对象时,不需要维持,所以不需要参与序列化,即用该关键字修饰。(该关键字修饰的实例变量不参与序列化)
然后呢,看一下ArrayList有哪些构造器:
private static final int DEFAULT_CAPACITY = 10;//默认容量大小为10 private int size;//提供私有变量保存数组的长度 /** *接收一个int型的整数,初始化数组容量 */ 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); } } /** * 不接收任何参数,采用默认容量10 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 接收一个Collection类型的集合,把集合的元素复制到数组中 * */ 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的扩容:
ensureCapacity()方法是用来首先计算出一个最小扩展的容量minExpand,然后所需要的最小容量和最小扩展容量相比较,如果大于,就会进入ensureExplicitCapacity方法,也是进行判断后,然后进入主要的方法grow方法,计算新的容量大小为旧容量+旧容量/2(右移一位相当于除以2)即为原来长度的1.5倍,为什么是1.5倍呢?因为由实验可得出,1.5倍不会浪费太大的内存。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
除此之外呢,还有一个方法,这个方法是来判断数组中元素个数和数组长度的,来进行适时的释放内存空间,适时的调用此方法减少内存占用:
public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
从源码中可以看出,ArrayList没有实现任何的同步,所以不是线程安全的,其他的添加,删除方法可以自行点开源码查看,相应的很简单。
以上是关于java集合核心源码01——ArrayList的主要内容,如果未能解决你的问题,请参考以下文章