[置顶] Java容器学习--ArrayList源码分析
2018-01-12 11:00
543 查看
ArrayList 内部采用数组实现,是一种顺序存储方式,并且支持随机访问。本文分析基于 JDK 1.8.0_151 版本。
在执行
调用关系:
grow 方法:
ensureExplicitCapacity 方法:
ensureCapacityInternal 方法:
ensureCapacityInternal()方法首先判断该数组此时是不是默认初始数组,如果是的话就判断一下minCapacity是否大于10,如果不是就扩容到初始默认数组长度10,如果是就直接扩容到minCapacity值的长度。
用户所能使用的只有一个公有方法,也就是说可以由用户显式执行扩容操作:
而
使用 ArrayList 需要注意的地方:
1.ArrayList 是基于数组的方式实现的,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
2.ArrayList在插入元素时,可能会进行数组的扩容,但是在删除元素时却不会减小数组的容量,如果希望减小数组的容量,可使用 trimToSize 方法,在查找元素要遍历数组时,对非null元素使用equals方法,对null元素使用==。
3.扩充容量的方法 ensureCapacityInternal。ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当 容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍,如果设置后的新容量还不够,则直接把新容量设置为传入的参数(也就是所需的容 量),而后用Arrays.copyof()方法将元素拷贝到新的数组。从中可以看出,当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList
4.ArrayList不是线程安全的。
参考:
https://zhuanlan.zhihu.com/p/27873515
https://zhuanlan.zhihu.com/p/28545284
https://mjd507.github.io/2017/04/09/Data-Structure-ArrayList-SourceCode/
http://www.cnblogs.com/skywang12345/p/3308556.html
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
ArrayList继承于
AbstractList并且实现了
List,
RandomAccess,
Cloneable,
Serializable接口。
属性
// 序列化ID private static final long serialVersionUID = 8683452581122892189L; // 默认初始化容量(只有执行 add 操作时才执行初始化) private static final int DEFAULT_CAPACITY = 10; // 空数组 private static final Object[] EMPTY_ELEMENTDATA = {}; // 默认的空数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 以上两个空数组只是为了区别于调用不同的构造方法 // 存放数据的内部数组(不参与序列化) transient Object[] elementData; // ArrayList 中有效数据的个数(并不一定就是elementData.length) private int size; // 数组最大长度 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
方法
构造方法
// 构造具有指定初始容量的空列表 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; } // 构造一个包含指定 collection 的元素的列表,这些元素是按照该collection 的迭代器返回它们的顺序排列的 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // 该 bug 分析见 http://blog.csdn.net/x_iya/article/details/78313756 // c.toArray might (incorrectly) not return Object[] (see 6260652) // c.toArray 不一定返回 Object[],也有可能是 String[](List<String> list = new ArrayList<>(Arrays.asList("111", "222"));) if (elementData.getClass() != Object[].class) // 转换为Object[] elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
增
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; } public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }
在执行
add相关操作时,需要调用
ensureCapacityInternal方法以确保
elementData有足够的空间保存元素。
删
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); elementData[--size] = null; // clear to let GC do its work return oldValue; }
改&查
ArrayList的修改和获取元素的方法相当简单,就是对
elementData数组进行相应的操作罢了。
改
public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
查
public E get(int index) { rangeCheck(index); return elementData(index); }
其他
扩容
扩容操作基本上是以数组作为其存储结构的类的核心了(String、ArrayList、StringBuilder,StringBuffer)。其他的所有操作无非就是围绕这个可变长的数组。调用关系:
ensureCapacity() //可以由用户显式调用 ensureCapacityInternal() // 由 ArrayList 内部隐式调用 --> ensureExplicitCapacity() --> grow()
grow 方法:
/** * 增加容量以确保它至少可以容纳最小容量参数 minCapacity 指定的元素数量 * minCapacity: 数组需要的最小容量值 * newCapacity: 就是扩容后实际的容量值 * 每次自动扩容都是扩大原有容量 1.5 倍 * 当自动扩容后的容量值仍然小于所需要的容量时,就直接扩容到所需容量值 */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); // 可以看到首先扩展为原来的 1.5 倍 if (newCapacity - minCapacity < 0) // 防止溢出 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 设置大小为 Integer.MAX_VALUE // minCapacity is usually close to size, so this is a win: 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; }
ensureExplicitCapacity 方法:
/** * 保证扩容值大于原数组长度才执行 grow() 方法 */ private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
ensureCapacityInternal 方法:
private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
ensureCapacityInternal()方法首先判断该数组此时是不是默认初始数组,如果是的话就判断一下minCapacity是否大于10,如果不是就扩容到初始默认数组长度10,如果是就直接扩容到minCapacity值的长度。
用户所能使用的只有一个公有方法,也就是说可以由用户显式执行扩容操作:
/** * 如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数 minCapacity 所指定的元素个数 */ public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } }
而
ensureCapacityInternal隐式扩容方法在容器添加元素的时候被调用,也就是“懒扩容–>只有在真正添加元素的时候进行扩容操作”。
使用 ArrayList 需要注意的地方:
1.ArrayList 是基于数组的方式实现的,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
2.ArrayList在插入元素时,可能会进行数组的扩容,但是在删除元素时却不会减小数组的容量,如果希望减小数组的容量,可使用 trimToSize 方法,在查找元素要遍历数组时,对非null元素使用equals方法,对null元素使用==。
3.扩充容量的方法 ensureCapacityInternal。ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当 容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍,如果设置后的新容量还不够,则直接把新容量设置为传入的参数(也就是所需的容 量),而后用Arrays.copyof()方法将元素拷贝到新的数组。从中可以看出,当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList
4.ArrayList不是线程安全的。
参考:
https://zhuanlan.zhihu.com/p/27873515
https://zhuanlan.zhihu.com/p/28545284
https://mjd507.github.io/2017/04/09/Data-Structure-ArrayList-SourceCode/
http://www.cnblogs.com/skywang12345/p/3308556.html
相关文章推荐
- java学习之旅59--模拟ArrayList容器的底层实现_JDK源码分析ArrayList
- Java 容器源码分析之 ArrayList
- 【Java基础】Java学习之ArrayList源码常用方法分析
- Java集合源码学习笔记(二)ArrayList分析
- java并发容器CopyOnWriteArrayList实现原理及源码分析
- [置顶] [Java容器]HashMap实现原理和源码分析
- Java集合源码学习(二)ArrayList分析
- Java容器深入研究(jdk 1.8)--- ArrayList总结与源码分析
- java非并发容器ArrayList 和 LinkedList 优缺点比较及其实现源码分析
- java容器源码分析(三)——ArrayList
- java容器源码分析--ArrayList(JDK1.8)
- java核心基础--jdk源码分析学习--ArrayList
- Java concurrent Framework并发容器之CopyOnWriteArrayList(1.6)源码分析
- java util包学习(5) Vector 源码分析
- java ArrayList源码学习
- Java concurrent Framework并发容器之ConcurrentHashMap(JDK1.5)源码分析
- Java concurrent Framework并发容器之concurrent.atomic包源码分析
- java util包学习(9)HashMap源码分析
- Java concurrent Framework并发容器之ArrayBlockingQueue(1.6)源码分析
- java util包学习(8)Map源码分析