ArrayList源码解读
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList源码解读相关的知识,希望对你有一定的参考价值。
在端午节这个节日里,有一个特殊的任务,我带着你一起揭开“ArrayList”的真面目。从成员变量、构造函数、主要方法三部分,对ArrayList有进一步的认识,希望能够帮助你。
一、成员变量
//默认容量 private static final int DEFAULT_CAPACITY = 10; //空数组,当调用无参数构造函数的时候默认给个空数组 private static final Object[] EMPTY_ELEMENTDATA = {}; //真正保存数据的数组 private transient Object[] elementData; //数组的大小 private int size;
transient关键字:ArrayList实现了Serializable接口,而添加上transient关键字,则elementData对象就不可以序列化。
二、构造函数
-
无参构造方法
-
带有容量的构造方法
-
带参数Collection的构造方法
在参数Collection的构造方法中,只有当当得到的elementData的类名不是Object,才会执行复制操作,因为Java是动态绑定,所以getClass获取的并不一定是Object类型,有可能是它的子类。
/** * 初始化elementData * @param initialCapacity */ public ArrayList(int initialCapacity) { super(); //如果小于0,则抛出异常 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); //初始化elementData数组 this.elementData = new Object[initialCapacity]; } /** * 初始化为空的对象数组 */ public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; } public ArrayList(Collection<? extends E> c) { //将c容器转换为object类型的数组 elementData = c.toArray(); //更新size的大小 size = elementData.length; //当得到的elementData的类名不是Object,才会执行复制操作 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }
三、主要方法
1、add方法
//添加元素 public boolean add(E e) { //首先判断数组容量是否需要扩充 ensureCapacityInternal(size + 1); // Increments modCount!! //添加元素,更新size的大小 elementData[size++] = e; return true; }
ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时ensureCapacityInternal,才分配10个对象空间。
2、get方法
//通过下标获取元素 public E get(int index) { //判断下标的合理性 rangeCheck(index); //返回元素 return elementData(index); }
3、remove方法
在remove方法中,我们需要注意对null的特殊处理。
public boolean remove(Object o) { //如果为null对象,则特殊处理,使用==来判断 if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { //如果不为null,则使用equals方法来判断。 for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
四、其他常用的方法
removeAll:移除集合A和集合B的交集,即A-B。
retainAll:保留两个集合的交集。
五、ArrayList的大小是如何自动增加的?(面试题)
-
ensureCapacityInternal 是判断是否要扩容的方法
-
ensureExplicitCapacity
-
grow 具体的扩容方法
实现思想:
-
确定所需minCapacity和数组当前size的关系,如果minCapacity>size,则准备grow。
-
如果1.5size<minCapacity,则准备扩充为minCapacity。
-
minCapacity和MAX_ARRAY_SIZE 的关系,如果minCapacity>MAX_ARRAY_SIZE ,则需要判断minCapacity是否超出int类型所能表示的最大值。如果超出,则抛出OutOfMemoryError。
//确保数组能够存放的:minCapacity private void ensureCapacityInternal(int minCapacity) { //如果elementData是空对象数组,最小容量为minCapacity和DEFAULT_CAPACITY最大值 if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //判断是否需要扩容 ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { //增加被改变的次数 modCount++; // 最小容量大于elementData的长度,则需要扩充 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; //扩容至原来的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //判断新容量是否够用,如果不够则赋值为minCapacity if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //如果newCapacity超过MAX_ARRAY_SIZE if (newCapacity - MAX_ARRAY_SIZE > 0) //检查容量的int值是不是已经溢出。 newCapacity = hugeCapacity(minCapacity); // 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间 // 并将elementData的数据复制到新的内存空间 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; }
欢迎加入Java学习交流群点击:484757838
以上是关于ArrayList源码解读的主要内容,如果未能解决你的问题,请参考以下文章