ArrayList工做原理

1.前言

List接口中,重要的两个实现是ArrayList和LinkedList,其中ArrayList又比LinkedList经常使用。这是由于ArrayList的读取性能远远高于LinkedList。本篇博文将介绍ArrayList,稍后介绍LinkedList及其二者的区别。java

2.ArrayList简介

    2.1 ArrayList是什么

Resizable-array implementation of the List interface.
Implements all optional list operations, and permits all elements, including null.
In addition to implementing the List interface,this class provides methods to manipulate the size of the array that is used internally to store the list.

        ArrayList是实现了List接口的、大小可变的数组队列。可以实现全部List接口的可选操做,并容许包括 null 在内的全部元素。除了实现 List 接口外,此类还提供一些方法来存储列表的数组的大小。(此类大体上等同于 Vector 类,除了此类是不一样步的。)--JDK API翻译(可能不是很精确,请谅解)算法

        从API的定义中能够看出,ArrayList是一个数组队列,至关于动态数组。与数组相比,可以动态增加。api

    2.2 ArrayList的数据结构

 

 

 

 

 

 

 

 

 

java.util 
类 ArrayList<E>
java.lang.Object
  - java.util.AbstractCollection<E>
      - java.util.AbstractList<E>
          - java.util.ArrayList<E>
全部已实现的接口:
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess

    ArrayList继承了AbstractList,实现了List,它是一个数组队列,提供了相关的增、改、删数组

查等功能。安全

    ArrayList实现了RandomAccess接口,提供了随机访问功能。RandomAccess是List 实现所使用的标记接口,用来代表其支持快速(一般是固定时间)随机访问。此接口的主要目的是容许通常的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。(来自JDk API翻译)数据结构

    ArrayList实现了Cloneable接口,提供了克隆功能多线程

    ArrayList实现了java.io.Serializable借口,这意味着ArrayList支持序列化。并发

    注意:ArrayList是非线程安全的!因此,建议在多线程程序中避免使用ArrayList,可使用Vector或者CopyOnWriteArrayList。(这两个类的用法及其实现,在稍后的博文中将被介绍)app

3.源码解析ArrayList    

    3.1 ArrayList的重要属性

package java.util;

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

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer.
	 * 数组缓冲区中的数组的元素存储,即保存ArrayList中数据的数组 
     */
    private transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     * ArrayList中实际数据的数量
     * @serial
     */
    private int size;
}

       从源码中看到ArrayList有2个重要对象dom

  • elementData是“Object[]类型的数组”,它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,咱们经过带初始容量的构造器来执行构建数组。若是不含初始化容量的构造器,elementData的容量是10.elementData数组的大小会根据ArrayList容量的增加而动态增加。可是建议在添加大量元素前,应用程序可使用 ensureCapacity 操做来增长 ArrayList 实例的容量。这能够减小递增式再分配的数量。构造器的内容,详见3.2 ArrayList的构造函数。具体的增加方式,请参照3.3 ArrayList的重要方法中的ensureCapacity()函数解析
  • size 是动态数组的实际大小

     3.2 ArrayList的构造函数

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * 构造一个具备指定初始容量的空列表。
     */
    public ArrayList(int initialCapacity) {
	super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
	//新建一个数组,大小为入参值										   
	this.elementData = new Object[initialCapacity];
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     * ArrayList的无参构造器, 构造一个初始容量为 10 的空列表
     */
    public ArrayList() {
	this(10);
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。     */
    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)
	//Arrays.copyOf方法:数组复制
	    elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
}

    3.3 ArrayList的重要方法

        3.3.1 增长ArrayList容量的方法ensureCapacity()

            当ArrayList容量不足以容纳所有元素时,ArrayList会从新设置容量:新的容量 = (原始容量 X 3) / 2 + 1。再次说明在添加大量元素前,应用程序应该使用 ensureCapacity 操做来增长 ArrayList 实例的容量。以减小递增式再分配的数量。

/**
     * Increases the capacity of this <tt>ArrayList</tt> instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     * 增长此 ArrayList 实例的容量,以确保它至少可以容纳最小容量参数所指定的元素数。
     */
    public void ensureCapacity(int minCapacity) {
	//将修改统计计数 +1
	modCount++;
	int oldCapacity = elementData.length;
	//若是传入最小容量大于原来数组的长度,就增长arrayList容量
	//新的容量 = (原始容量 * 3)/2 + 1
	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);
	}
    }

        3.3.2 最小化 ArrayList 实例的存储量方法trimToSize()

        将此 ArrayList 实例的容量调整为列表的当前大小。应用程序可使用此操做来最小化 ArrayList 实例的存储量。

/**
     * Trims the capacity of this ArrayList instance to be the list's current size.
     * 将此 ArrayList 实例的容量调整为列表的当前大小,也即将当前容量值设置为实际元素个数
     */
    public void trimToSize() {
	modCount++;
	int oldCapacity = elementData.length;
	if (size < oldCapacity) {
            elementData = Arrays.copyOf(elementData, size);
	}
    }

        3.3.3 添加元素add()

        将指定的元素添加到此列表的尾部。在调整数组大小以前要判断是否须要调整arrayList容量

/**
     * Appends the specified element to the end of this list.
     *
     * 添加元素e
     */
    public boolean add(E e) {
	//肯定ArrayList容量大小
	ensureCapacity(size + 1);  // Increments modCount!!
	//添加e到ArrayList尾部
	elementData[size++] = e;
	return true;
    }

        3.3.4 检查是否含有指定元素的方法contains(object o)

/**
     * Returns true if this list contains the specified element.
     * 返回ArrayList是否包含Object(o)
     */
    public boolean contains(Object o) {
	return indexOf(o) >= 0;
    }

    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * 正向查找,返回元素的索引值,若是元素不存在集合中,就返回-1
     */
    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;
    }

        3.3.5 其他方法详见源码(带翻译)

package java.util;

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 正向查找,返回元素的索引值
    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位置的值为element
    public E set(int index, E element) {
        RangeCheck(index);

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

    // 将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;
    }

    // 克隆函数
    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();
        }
    }
}

4. ArrayList遍历方式

    集合操做中最多见的就是遍历操做了,ArrayList支持3种遍历方式:迭代器遍历、随机访问和for循环|(foreach循环)

    4.1 迭代器遍历,即经过Iterator去遍历(效率低下)

Integer value = null;
Iterator iter = list.iterator();
while (iter.hasNext()) {
    value = (Integer)iter.next();
}

    4.2 随机访问遍历,经过索引值去遍历(效率高)

Integer value = null;
int size = list.size();
for (int i=0; i<size; i++) {
    value = (Integer)list.get(i);        
}

    4.3 for循环遍历,效率介于两者之间

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

    4.4 三种遍历效率比对

package com.my.mobile;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TestArrayList {
	public static void main(String[] args) {
		TestArrayList t = new TestArrayList();
		List<Data> initArrayList = t.initArrayList(100000);
		testiterator(initArrayList);
		testRandomAccess(initArrayList);
		testforeach(initArrayList);
		
	}
	/**
	 * 测试foreach循环遍历ArrayList性能
	 * @param initArrayList
	 * */
	private static void testforeach(List<Data> initArrayList) {
		// TODO Auto-generated method stub
		long starttime = System.currentTimeMillis();
		for (Data data : initArrayList) {
//			System.out.println(data.toString());
		}
		System.out.println("foreach遍历完毕,耗时["+(System.currentTimeMillis() - starttime)+"]ms");
	}

	private static void testRandomAccess(List<Data> initArrayList) {
		// TODO Auto-generated method stub
		long starttime = System.currentTimeMillis();
		for(int i = 0;i<initArrayList.size();i++){
			Data data = initArrayList.get(i);
//			System.out.println(data.toString());
		}
		System.out.println("randomAccess遍历完毕,耗时["+(System.currentTimeMillis() - starttime)+"]ms");
	}

	private static void testiterator(List<Data> initArrayList) {
		// TODO Auto-generated method stub
		long starttime = System.currentTimeMillis();
		for(Iterator<Data> iter = initArrayList.iterator();iter.hasNext();){
			Data data = iter.next();
//			System.out.println(data.toString());
		}
		System.out.println("iterator遍历完毕,耗时["+(System.currentTimeMillis() - starttime)+"]ms");
	}

	private List<Data> initArrayList(int listSize){
		Data data = new Data();
		List<Data> arrayList = new ArrayList<Data>();
		for(int i = 0;i<listSize;i++){
			try {
				TestArrayList.Data data_clone = data.clone();
				data_clone.setId(i+"");
				data_clone.setName("我是第"+i+"个酷酷的酷");
				data_clone.setUser("kucs");
				arrayList.add(data_clone);
			} catch (CloneNotSupportedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return arrayList;
	}
	class Data implements Cloneable{
		private String id;
		private String name;
		private String user;

		public Data() {
			super();
			// TODO Auto-generated constructor stub
		}

		public String getId() {
			return id;
		}

		public void setId(String id) {
			this.id = id;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getUser() {
			return user;
		}

		public void setUser(String user) {
			this.user = user;
		}

		@Override
		public String toString() {
			return "Data [id=" + id + ", name=" + name + ", user=" + user + "]";
		}

		@Override
		protected Data clone() throws CloneNotSupportedException {
			// TODO Auto-generated method stub
			return (Data)super.clone();
		}
		
	}
}

运行结果:

iterator遍历完毕,耗时[34]ms
randomAccess遍历完毕,耗时[12]ms
foreach遍历完毕,耗时[14]ms

    经此实验,遍历ArrayList时,使用随机访问,遍历比较快。

5.ArrayList总结

  •     ArrayList是实现了List接口的、大小可变的数组队列。可以实现全部List可选操做,并容许包括null在内的全部元素。除了实现List接口外,此类还提供一些方法来操做内部用来存储列表的数组的大小。(此类大体上等同于 Vector 类,除了此类是不一样步的。)
  • ArrayList的size、isEmpty、get、set、iterator和listIterator操做都以固定时间运行。add操做以分摊的固定时间运行,也就是说,添加n个元素须要O(n)时间。其余全部操做都以线性时间运行。与用于LinkedList实现的常数因子相比,此实现的常数因子较低。
  • 每一个ArrayList实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它老是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增加。并未指定增加策略的细节,由于这不仅是添加元素会带来分摊固定时间开销那样简单。在添加大量元素前,应用程序可使用ensureCapacity操做来增长ArrayList实例的容量。这能够减小递增式再分配的数量。
  • 注意,此实现不是同步的。若是多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操做,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这通常经过对天然封装该列表的对象进行同步操做来完成。若是不存在这样的对象,则应该使用Collections.synchronizedList方法将该列表“包装”起来。这最好在建立时完成,以防止意外对列表进行不一样步的访问。尽可能避免在并发访问中使用ArrayList。可使用同步的Vector或者CopyOnWriteList集合代替ArrayList
相关文章
相关标签/搜索