深入理解ArrayList

目录
导读
(一)什么是ArrayList
1.1 定义

1.2 构造方法

1.3 API概览

(二)如何实现ArrayList(原理)

2.1 继承关系

2.2 类图关系(与Collection的关系)

(三)源码分析
3.1 属性
3.2 构造函数
3.3 初始化
3.4 具体方法
3.5 扩容策略
(四)遍历方式
(五)toArray()的异常
(六)时间复杂度

导读
有相关的统计,ArrayList是Java编程人员使用最多的类了,可能除了String之外的。所以很有必要深入地了解它。
(一)什么是ArrayList
1.1 定义
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

ArrayList 是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。既然实现了List接口,那它自然也就有了有序性和可重复性地特点,并提供了相关的添加、删除、修改、遍历等功能;另外ArrayList 也实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。稍后,我们会比较List的“快速随机访问”和“通过Iterator迭代器访问”的效率。

ArrayList 还实现了Cloneable接口,即覆盖了函数clone(),能被克隆;ArrayList 同样实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
与Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。

1.2 构造方法

ArrayList()
//Constructs an empty list with an initial capacity of ten.
ArrayList(Collection<? extends E> c)
//Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.
ArrayList(int initialCapacity)
//Constructs an empty list with the specified initial capacity.
1.3 API概览

// Collection中定义的API
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()
Iterator<E>         iterator()
boolean             remove(Object object)
boolean             removeAll(Collection<?> collection)
boolean             retainAll(Collection<?> collection)
int                 size()
<T> T[]             toArray(T[] array)
Object[]            toArray()
// AbstractCollection中定义的API
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新增的API
Object               clone()
void                 ensureCapacity(int minimumCapacity)
void                 trimToSize()
void                 removeRange(int fromIndex, int toIndex)
(二)如何实现ArrayList(原理)
2.1 继承关系

java.lang.Object
	java.util.AbstractCollection<E>
		java.util.AbstractList<E>
			java.util.ArrayList<E>
2.2 类图关系(与Collection的关系)

ArrayList包含了两个重要的对象:elementData 和 size。
(01) elementData 是"Object[]类型的数组",它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。
elementData数组的大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,请参考源码分析中的ensureCapacity()函数。
(02) size 则是动态数组的实际大小。


(三)源码分析
3.1 属性

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {  
	// 序列化id  
	private static final long serialVersionUID = 8683452581122892189L;  
	// 默认初始的容量  
	private static final int DEFAULT_CAPACITY = 10;  
	// 一个空对象  
	private static final Object[] EMPTY_ELEMENTDATA = new Object[0];  
	// 一个空对象,如果使用默认构造函数创建,则默认对象内容默认是该值  
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];  
	// 当前数据对象存放地方,当前对象不参与序列化  
	transient Object[] elementData;  
	// 当前数组长度  
	private int size;  
	// 数组最大长度  
	private static final int MAX_ARRAY_SIZE = 2147483639;  

	// 省略方法。。  
}  
3.2 构造函数

public ArrayList() {  
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 
}  
注意:此时我们创建的ArrayList对象中的elementData中的长度是1,size是0,当进行第一次add的时候,elementData将会变成默认的长度:10。

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);  
	}  
}  
如果传入参数,则代表指定ArrayList的初始数组长度,传入参数如果是大于等于0,则使用用户的参数初始化,如果用户传入的参数小于0,则抛出异常。
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;  
	}  
}  
(1)将collection对象转换成数组,然后将数组的地址的赋给elementData。
(2)更新size的值,同时判断size的大小,如果是size等于0,直接将空对象EMPTY_ELEMENTDATA的地址赋给elementData
(3)如果size的值大于0,则执行Arrays.copy方法,把collection对象的内容(可以理解为深拷贝)copy到elementData中。

注意:this.elementData = arg0.toArray(); 这里执行的简单赋值时浅拷贝,所以要执行Arrays.copyOf做深拷贝。


3.3 初始化

https://zhuanlan.zhihu.com/p/27873515
这篇文章讲的很具体,从JVM的内存管理角度详细地介绍了,ArrayList地初始化过程。

3.4 具体方法

package java.util;

	public class ArrayList<E> extends AbstractList<E>
			implements List<E>, RandomAccess, Cloneable, java.io.Serializable
	{
		// 序列版本号
		private static final long serialVersionUID = 8683452581122892189L;

		// 保存ArrayList中数据的数组
		private transient Object[] elementData;

		// ArrayList中实际数据的数量
		private int size;

		// ArrayList带容量大小的构造函数。
		public ArrayList(int initialCapacity) {
			super();
			if (initialCapacity < 0)
				throw new IllegalArgumentException("Illegal Capacity: "+
												   initialCapacity);
			// 新建一个数组
			this.elementData = new Object[initialCapacity];
		}

		// ArrayList构造函数。默认容量是10。
		public ArrayList() {
			this(10);
		}

		// 创建一个包含collection的ArrayList
		public ArrayList(Collection<? extends E> c) {
			elementData = c.toArray();
			size = elementData.length;
			// c.toArray might (incorrectly) not return Object[] (see 6260652)
			if (elementData.getClass() != Object[].class)
				elementData = Arrays.copyOf(elementData, size, Object[].class);
		}


		// 将当前容量值设为 =实际元素个数
		public void trimToSize() {
			modCount++;
			int oldCapacity = elementData.length;
			if (size < oldCapacity) {
				elementData = Arrays.copyOf(elementData, size);
			}
		}


		// 确定ArrarList的容量。
		private void ensureCapacityInternal(int minCapacity) {
			// 通过ArrayList<Integer> a = new ArrayList<Integer>()或者通过序列化读取,元素大小为0时,底层数组才会为null数组
			// 如果底层数组大小为0,则使用默认的容量大小10
			if (elementData == EMPTY_ELEMENTDATA) {
				minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
			}

			ensureExplicitCapacity(minCapacity);
		}

		private void ensureExplicitCapacity(int minCapacity) {
			// 数据结构发生改变,和fail-fast机制有关,在使用迭代器过程中,只能通过迭代器的方法(比如迭代器中add,remove等),
			//修改List的数据结构,
			// 如果使用List的方法(比如List中的add,remove等),修改List的数据结构,会抛出ConcurrentModificationException
			modCount++;  


			// 当前数组容量大小不足时,才会调用grow方法,自动扩容
			if (minCapacity - elementData.length > 0)
				grow(minCapacity);
		}

		private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


		private void grow(int minCapacity) {
			// overflow-conscious code
			int oldCapacity = elementData.length;
			// 新的容量大小 = 原容量大小的1.5倍
			int newCapacity = oldCapacity + (oldCapacity >> 1);
			if (newCapacity - minCapacity < 0) //溢出判断,比如minCapacity = Integer.MAX_VALUE / 2, oldCapacity = minCapacity - 1
				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);
		}

		private static int hugeCapacity(int minCapacity) {
			if (minCapacity < 0) // overflow
				throw new OutOfMemoryError();
			return (minCapacity > MAX_ARRAY_SIZE) ?
				Integer.MAX_VALUE :
				MAX_ARRAY_SIZE;
		}

		// 添加元素e
		public boolean add(E e) {
			// 确定ArrayList的容量大小
			ensureCapacity(size + 1);  // Increments modCount!!
			// 添加e到ArrayList中
			elementData[size++] = e;
			return true;
		}

		// 返回ArrayList的实际大小
		public int size() {
			return size;
		}

		// 返回ArrayList是否包含Object(o)
		public boolean contains(Object o) {
			return indexOf(o) >= 0;
		}

		// 返回ArrayList是否为空
		public boolean isEmpty() {
			return size == 0;
		}

		// 正向查找,返回元素的索引值
		public int indexOf(Object o) {
			if (o == null) {
				for (int i = 0; i < size; i++)
				if (elementData[i]==null)
					return i;
				} else {
					for (int i = 0; i < size; i++)
					if (o.equals(elementData[i]))
						return i;
				}
				return -1;
			}

			// 反向查找,返回元素的索引值
			public int lastIndexOf(Object o) {
			if (o == null) {
				for (int i = size-1; i >= 0; i--)
				if (elementData[i]==null)
					return i;
			} else {
				for (int i = size-1; i >= 0; i--)
				if (o.equals(elementData[i]))
					return i;
			}
			return -1;
		}

		// 反向查找(从数组末尾向开始查找),返回元素(o)的索引值
		public int lastIndexOf(Object o) {
			if (o == null) {
				for (int i = size-1; i >= 0; i--)
				if (elementData[i]==null)
					return i;
			} else {
				for (int i = size-1; i >= 0; i--)
				if (o.equals(elementData[i]))
					return i;
			}
			return -1;
		}
	 

		// 返回ArrayList的Object数组
		public Object[] toArray() {
			return Arrays.copyOf(elementData, size);
		}

		// 返回ArrayList的模板数组。所谓模板数组,即可以将T设为任意的数据类型
		public <T> T[] toArray(T[] a) {
			// 若数组a的大小 < ArrayList的元素个数;
			// 则新建一个T[]数组,数组大小是“ArrayList的元素个数”,并将“ArrayList”全部拷贝到新数组中
			if (a.length < size)
				return (T[]) Arrays.copyOf(elementData, size, a.getClass());

			// 若数组a的大小 >= ArrayList的元素个数;
			// 则将ArrayList的全部元素都拷贝到数组a中。
			System.arraycopy(elementData, 0, a, 0, size);
			if (a.length > size)
				a[size] = null;
			return a;
		}

		// 获取index位置的元素值
		public E get(int index) {
			RangeCheck(index);

			return (E) elementData[index];
		}

		// 设置index位置的值为element
		public E set(int index, E element) {
			RangeCheck(index);

			E oldValue = (E) elementData[index];
			elementData[index] = element;
			return oldValue;
		}

		// 将e添加到ArrayList中
		public boolean add(E e) {
			ensureCapacity(size + 1);  // Increments modCount!!
			elementData[size++] = e;
			return true;
		}

		// 将e添加到ArrayList的指定位置
		public void add(int index, E element) {
			if (index > size || index < 0)
				throw new IndexOutOfBoundsException(
				"Index: "+index+", Size: "+size);

			ensureCapacity(size+1);  // Increments modCount!!
			System.arraycopy(elementData, index, elementData, index + 1,
				 size - index);
			elementData[index] = element;
			size++;
		}

		// 删除ArrayList指定位置的元素
		public E remove(int index) {
			RangeCheck(index);

			modCount++;
			E oldValue = (E) elementData[index];

			int numMoved = size - index - 1;
			if (numMoved > 0)
				System.arraycopy(elementData, index+1, elementData, index,
					 numMoved);
			elementData[--size] = null; // Let gc do its work

			return oldValue;
		}

		// 删除ArrayList的指定元素
		public boolean remove(Object o) {
			if (o == null) {
					for (int index = 0; index < size; index++)
				if (elementData[index] == null) {
					fastRemove(index);
					return true;
				}
			} else {
				for (int index = 0; index < size; index++)
				if (o.equals(elementData[index])) {
					fastRemove(index);
					return true;
				}
			}
			return false;
		}


		// 快速删除第index个元素
		private void fastRemove(int index) {
			modCount++;
			int numMoved = size - index - 1;
			// 从"index+1"开始,用后面的元素替换前面的元素。
			if (numMoved > 0)
				System.arraycopy(elementData, index+1, elementData, index,
								 numMoved);
			// 将最后一个元素设为null
			elementData[--size] = null; // Let gc do its work
		}

		// 删除元素
		public boolean remove(Object o) {
			if (o == null) {
				for (int index = 0; index < size; index++)
				if (elementData[index] == null) {
					fastRemove(index);
				return true;
				}
			} else {
				// 便利ArrayList,找到“元素o”,则删除,并返回true。
				for (int index = 0; index < size; index++)
				if (o.equals(elementData[index])) {
					fastRemove(index);
				return true;
				}
			}
			return false;
		}

		// 清空ArrayList,将全部的元素设为null
		public void clear() {
			modCount++;

			for (int i = 0; i < size; i++)
				elementData[i] = null;

			size = 0;
		}

		// 将集合c追加到ArrayList中
		public boolean addAll(Collection<? extends E> c) {
			Object[] a = c.toArray();
			int numNew = a.length;
			ensureCapacity(size + numNew);  // Increments modCount
			System.arraycopy(a, 0, elementData, size, numNew);
			size += numNew;
			return numNew != 0;
		}

		// 从index位置开始,将集合c添加到ArrayList
		public boolean addAll(int index, Collection<? extends E> c) {
			if (index > size || index < 0)
				throw new IndexOutOfBoundsException(
				"Index: " + index + ", Size: " + size);

			Object[] a = c.toArray();
			int numNew = a.length;
			ensureCapacity(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;
		}

		// 删除fromIndex到toIndex之间的全部元素。
		protected void removeRange(int fromIndex, int toIndex) {
		modCount++;
		int numMoved = size - toIndex;
			System.arraycopy(elementData, toIndex, elementData, fromIndex,
							 numMoved);

		// Let gc do its work
		int newSize = size - (toIndex-fromIndex);
		while (size != newSize)
			elementData[--size] = null;
		}

		private void RangeCheck(int index) {
		if (index >= size)
			throw new IndexOutOfBoundsException(
			"Index: "+index+", Size: "+size);
		}


		// 克隆函数
		public Object clone() {
			try {
				ArrayList<E> v = (ArrayList<E>) super.clone();
				// 将当前ArrayList的全部元素拷贝到v中
				v.elementData = Arrays.copyOf(elementData, size);
				v.modCount = 0;
				return v;
			} catch (CloneNotSupportedException e) {
				// this shouldn't happen, since we are Cloneable
				throw new InternalError();
			}
		}


		// java.io.Serializable的写入函数
		// 将ArrayList的“容量,所有的元素值”都写入到输出流中
		private void writeObject(java.io.ObjectOutputStream s)
			throws java.io.IOException{
		// Write out element count, and any hidden stuff
		int expectedModCount = modCount;
		s.defaultWriteObject();

			// 写入“数组的容量”
			s.writeInt(elementData.length);

		// 写入“数组的每一个元素”
		for (int i=0; i<size; i++)
				s.writeObject(elementData[i]);

		if (modCount != expectedModCount) {
				throw new ConcurrentModificationException();
			}

		}


		// java.io.Serializable的读取函数:根据写入方式读出
		// 先将ArrayList的“容量”读出,然后将“所有的元素值”读出
		private void readObject(java.io.ObjectInputStream s)
			throws java.io.IOException, ClassNotFoundException {
			// Read in size, and any hidden stuff
			s.defaultReadObject();

			// 从输入流中读取ArrayList的“容量”
			int arrayLength = s.readInt();
			Object[] a = elementData = new Object[arrayLength];

			// 从输入流中将“所有的元素值”读出
			for (int i=0; i<size; i++)
				a[i] = s.readObject();
		}
	}
(01) ArrayList 实际上是通过一个数组去保存数据的。当我们构造ArrayList时;若使用默认构造函数,则ArrayList的默认容量大小是10。
(02) 当ArrayList容量不足以容纳全部元素时,ArrayList会重新设置容量:新的容量=原始容量的1.5倍。
(03) ArrayList的克隆函数,即是将全部元素克隆到一个数组中。
(04) ArrayList实现java.io.Serializable的方式。当写入到输出流时,先写入“容量”,再依次写入“每一个元素”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。

3.5 扩容策略
在上一小节中对源码的分析中,已经提及到当ArrayList容量不足以容纳全部元素时,ArrayList会重新设置容量:新的容量=原始容量的1.5倍。更详细的内容见此博客:https://zhuanlan.zhihu.com/p/27878015

(四)遍历方式

ArrayList支持3种遍历方式
(01) 第一种,通过迭代器遍历。即通过Iterator去遍历。

Integer value = null;
Iterator iter = list.iterator();
while (iter.hasNext()) {
	value = (Integer)iter.next();
}
(02) 第二种,随机访问,通过索引值去遍历。
由于ArrayList实现了RandomAccess接口,它支持通过索引值去随机访问元素。

Integer value = null;
int size = list.size();
for (int i=0; i<size; i++) {
	value = (Integer)list.get(i); 
}
(03) 第三种,for循环遍历。如下:

Integer value = null;
for (Integer integ:list) {
value = integ;
}

下面通过一个实例,比较这3种方式的效率,实例代码(ArrayListRandomAccessTest.java)如下:

import java.util.*;
import java.util.concurrent.*;


/*
 * @desc ArrayList遍历方式和效率的测试程序。
 *
 * @author skywang
 */
public class ArrayListRandomAccessTest {


	public static void main(String[] args) {
		List list = new ArrayList();
		for (int i=0; i<100000; i++)
			list.add(i);
		//isRandomAccessSupported(list);
		iteratorThroughRandomAccess(list) ;
		iteratorThroughIterator(list) ;
		iteratorThroughFor2(list) ;
	
	}


	private static void isRandomAccessSupported(List list) {
		if (list instanceof RandomAccess) {
			System.out.println("RandomAccess implemented!");
		} else {
			System.out.println("RandomAccess not implemented!");
		}


	}


	public static void iteratorThroughRandomAccess(List list) {


		long startTime;
		long endTime;
		startTime = System.currentTimeMillis();
		for (int i=0; i<list.size(); i++) {
			list.get(i);
		}
		endTime = System.currentTimeMillis();
		long interval = endTime - startTime;
		System.out.println("iteratorThroughRandomAccess:" + interval+" ms");
	}


	public static void iteratorThroughIterator(List list) {


		long startTime;
		long endTime;
		startTime = System.currentTimeMillis();
		for(Iterator iter = list.iterator(); iter.hasNext(); ) {
			iter.next();
		}
		endTime = System.currentTimeMillis();
		long interval = endTime - startTime;
		System.out.println("iteratorThroughIterator:" + interval+" ms");
	}




	public static void iteratorThroughFor2(List list) {


		long startTime;
		long endTime;
		startTime = System.currentTimeMillis();
		for(Object obj:list)
			;
		endTime = System.currentTimeMillis();
		long interval = endTime - startTime;
		System.out.println("iteratorThroughFor2:" + interval+" ms");
	}
}
运行结果:
iteratorThroughRandomAccess:3 ms
iteratorThroughIterator:8 ms
iteratorThroughFor2:5 ms

由此可见,遍历ArrayList时,使用随机访问(即,通过索引序号访问)效率最高,而使用迭代器的效率最低!


(五)toArray()的异常

当我们调用ArrayList中的 toArray(),可能遇到过抛出“java.lang.ClassCastException”异常的情况。下面我们说说这是怎么回事。

ArrayList提供了2个toArray()函数:

Object[] toArray()
<T> T[] toArray(T[] contents)
调用 toArray() 函数会抛出“java.lang.ClassCastException”异常,但是调用 toArray(T[] contents) 能正常返回 T[]。
toArray() 会抛出异常是因为 toArray() 返回的是 Object[] 数组,将 Object[] 转换为其它类型(如如,将Object[]转换为的Integer[])则会抛出“java.lang.ClassCastException”异常,因为Java不支持向下转型。具体的可以参考前面ArrayList.java的源码介绍部分的toArray()。
解决该问题的办法是调用 <T> T[] toArray(T[] contents) , 而不是 Object[] toArray()。
调用 toArray(T[] contents) 返回T[]的可以通过以下几种方式实现。

// toArray(T[] contents)调用方式一
public static Integer[] vectorToArray1(ArrayList<Integer> v) {
	Integer[] newText = new Integer[v.size()];
	v.toArray(newText);
	return newText;
}

// toArray(T[] contents)调用方式二。最常用!
public static Integer[] vectorToArray2(ArrayList<Integer> v) {
	Integer[] newText = (Integer[])v.toArray(new Integer[0]);
	return newText;
}

// toArray(T[] contents)调用方式三
public static Integer[] vectorToArray3(ArrayList<Integer> v) {
	Integer[] newText = new Integer[v.size()];
	Integer[] newStrings = (Integer[])v.toArray(newText);
	return newStrings;
}
(六)时间复杂度
https://zhuanlan.zhihu.com/p/27952014
https://zhuanlan.zhihu.com/p/27938717
https://zhuanlan.zhihu.com/p/28016098

参考文章: http://www.cnblogs.com/skywang12345/p/3308556.html

猜你喜欢

转载自blog.csdn.net/adelaide_guo/article/details/78669840
今日推荐