ArrayList
Posted tkzl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList相关的知识,希望对你有一定的参考价值。
前言
集合的作用就是以一定的方式组织、存储数据。
分析集合,有四点要特别注意:
是否线程安全
是否允许存储 null
是否允许存储重复数据
是否有序,有序的意思是读取数据的顺序和存放数据的顺序一致。
ArrayList 基本结构
首先我们来看一下 ArrayList 在 Collection 集合框架中的结构图,蓝线代表继承关系,绿线代表接口实现。
从结构图来看,ArrayList 类继承了 AbstractList 具备了添加、删除、修改、遍历等功能;实现了 RandomAccess 接口,提供了随机访问功能,也就是通过索引快速访问数组元素;实现了 Cloneable 接口,重写 clone() 函数,可以被复制;实现了 Serializable 接口,支持序列化。
ArrayList 底层存储数据结构是通过数组实现的。接下来,我们看一下 ArrayList 里面有哪些重要的属性。
ArrayList 属性
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = ;
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = ;
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
ArrayList 方法概要
属于 Collection 接口的方法
boolean add(E object)
boolean addAll(Collection<? extends E> collection)
void clear()
boolean contains(Object object)
boolean containsAll(Collection<?> collection)
boolean equals(Object object)
int hashCode()
boolean isEmpty()
boolean remove(Object object)
boolean removeAll(Collection<?> collection)
boolean retainAll(Collection<?> collection)
int size()
<T> T[] toArray(T[] array)
Object[] toArray()
属于 AbstractCollection 抽象类中的方法
void add(int location, E object)
boolean addAll(int location, Collection<? extends E> collection)
E get(int location)
int indexOf(Object object)
int lastIndexOf(Object object)
ListIterator<E> listIterator(int location)
ListIterator<E> listIterator()
E remove(int location)
E set(int location, E object)
List<E> subList(int start, int end)
属于 ArrayList 自己的方法
Object clone()
void ensureCapacity(int minimumCapacity)
void trimToSize()
void removeRange(int fromIndex, int toIndex)
从上面的类的 API 来看事实上涉及到集合的本身的东西基本就在 Collection 接口中定义好了。
接下来我们就要看看源码了。
ArrayList 源码分析
构造方法
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList()
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c)
elementData = c.toArray();
if ((size = elementData.length) != 0)
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
else
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
接下来看一下 add(E e) 方法
public boolean add(E e)
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
private void ensureCapacityInternal(int minCapacity) // 最小容量 = 当前数组元素个数 + 1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
ensureExplicitCapacity(minCapacity);
如果初始化集合的时候并未设置集合容量,那么最小容量大于当前数组元素个数其实是必然的,因为 minCapacity = size + 1,而 elementData.lenth = size。所以对于未初始化容量的集合而言,每次 add 的时候都会进行扩容,复制的操作。如果初始化时始设置了 elementData 数组的长度为 20,那么在这里就不会成立,下面就不会进行 grow 扩容,也不会继续数组赋值,所以如果我们能够预测我们的 arrayList 存储多少值,我们就先分配好
数组的容量,后面就不会每次添加元素的收进行扩容。
private void ensureExplicitCapacity(int minCapacity)
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) // 如果最小容量大于当前数组长度则扩容
grow(minCapacity);
扩容的时候会去调用 grow() 方法来进行动态扩容,在 grow() 方法中采用了位运算,我们知道位运算的速度远远快于整除运算;
有一点需要注意的是,容量拓展,是创建一个新的数组,然后将旧数组上的元素 copy 到新数组,这是一个很大的消耗,所以在我们使用 ArrayList 时,最好能预计数据量。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 这个方法就是是动态扩展的精髓
private void grow(int minCapacity)
// overflow-conscious code
int oldCapacity = elementData.length;
// 将 oldCapacity 右移一位,其效果相当于 oldCapacity/2,整句的结果就是设置新数组的容量为原来数组的 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组,
// 不够就将数组长度设置为需要的长度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将原来数组的值 copy 新数组中去, ArrayList 的引用指向新数组
// 这会新创建数组,如果数据量很大,重复的创建的数组,那么还是会影响效率,
// 因此鼓励在合适的时候通过构造方法指定默认的 capaticy 大小
// 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;
看一下 JDK1.6 的动态扩容的实现原理:
public void ensureCapacity(int minCapacity)
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity)
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
从代码上,我们可以看出区别:
在容量进行扩展的时候,其实例如整除运算将容量扩展为原来的 1.5 倍加 1,而 jdk1.8 是利用位运算,从效率上,jdk1.8 就要快于 jdk1.6。
在算出 newCapacity 时,其没有和 ArrayList 所定义的 MAX_ARRAY_SIZE 作比较,为什么没有进行比较呢,原因是 jdk1.8 没有定义这个MAX_ARRAY_SIZE 最大容量,也就是说,没有最大容量限制的,但是 jdk1.8 做了一个改进,进行了容量限制。
以上是关于ArrayList的主要内容,如果未能解决你的问题,请参考以下文章