转载:http://wiki.jikexueyuan.com/project/java-collection/arraylist.html

ArrayList 概述

ArrayList 可以理解为动态数组,用 MSDN 中的说法,就是 Array 的复杂版本。与 Java 中的数组相比,它的容量能动态增长。ArrayList 是 List 接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)

每个 ArrayList 实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造 ArrayList 时指定其容量。在添加大量元素前,应用程序也可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量,这可以减少递增式再分配的数量。

注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)

我们先学习了解其内部的实现原理,才能更好的理解其应用。

ArrayList 的实现

对于 ArrayList 而言,它实现 List 接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。下面我们来分析 ArrayList 的源代码:

实现的接口

  1. public class ArrayList<E> extends AbstractList<E>
  2. implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  3. {
  4. }

ArrayList 继承了 AbstractList,实现了 List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。

ArrayList 实现了 RandmoAccess 接口,即提供了随机访问功能。RandmoAccess 是 java 中用来被 List 实现,为 List 提供快速访问功能的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。

ArrayList 实现了 Cloneable 接口,即覆盖了函数 clone(),能被克隆。 ArrayList 实现 java.io.Serializable 接口,这意味着 ArrayList 支持序列化,能通过序列化去传输。

底层使用数组实现

  1. /**
  2. * The array buffer into which the elements of the ArrayList are stored.
  3. * The capacity of the ArrayList is the length of this array buffer.
  4. */
  5. private transient Object[] elementData;

构造方法

  1. /**
  2. * Constructs an empty list with an initial capacity of ten.
  3. */
  4. public ArrayList() {
  5. this(10);
  6. }
  7. /**
  8. * Constructs an empty list with the specified initial capacity.
  9. *
  10. * @param initialCapacity the initial capacity of the list
  11. * @throws IllegalArgumentException if the specified initial capacity
  12. * is negative
  13. */
  14. public ArrayList(int initialCapacity) {
  15. super();
  16. if (initialCapacity < 0)
  17. throw new IllegalArgumentException("Illegal Capacity: "+
  18. initialCapacity);
  19. this.elementData = new Object[initialCapacity];
  20. }
  21. /**
  22. * Constructs a list containing the elements of the specified
  23. * collection, in the order they are returned by the collection's
  24. * iterator.
  25. *
  26. * @param c the collection whose elements are to be placed into this list
  27. * @throws NullPointerException if the specified collection is null
  28. */
  29. public ArrayList(Collection<? extends E> c) {
  30. elementData = c.toArray();
  31. size = elementData.length;
  32. // c.toArray might (incorrectly) not return Object[] (see 6260652)
  33. if (elementData.getClass() != Object[].class)
  34. elementData = Arrays.copyOf(elementData, size, Object[].class);
  35. }

ArrayList 提供了三种方式的构造器:

  1. public ArrayList()可以构造一个默认初始容量为10的空列表;
  2. public ArrayList(int initialCapacity)构造一个指定初始容量的空列表;
  3. public ArrayList(Collection<? extends E> c)构造一个包含指定 collection 的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。

存储

ArrayList 中提供了多种添加元素的方法,下面将一一进行讲解:

1.set(int index, E element):该方法首先调用rangeCheck(index)来校验 index 变量是否超出数组范围,超出则抛出异常。而后,取出原 index 位置的值,并且将新的 element 放入 Index 位置,返回 oldValue。

  1. /**
  2. * Replaces the element at the specified position in this list with
  3. * the specified element.
  4. *
  5. * @param index index of the element to replace
  6. * @param element element to be stored at the specified position
  7. * @return the element previously at the specified position
  8. * @throws IndexOutOfBoundsException {@inheritDoc}
  9. */
  10. public E set(int index, E element) {
  11. rangeCheck(index);
  12. E oldValue = elementData(index);
  13. elementData[index] = element;
  14. return oldValue;
  15. }
  16. /**
  17. * Checks if the given index is in range. If not, throws an appropriate
  18. * runtime exception. This method does *not* check if the index is
  19. * negative: It is always used immediately prior to an array access,
  20. * which throws an ArrayIndexOutOfBoundsException if index is negative.
  21. */
  22. private void rangeCheck(int index) {
  23. if (index >= size)
  24. throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  25. }

2.add(E e):该方法是将指定的元素添加到列表的尾部。当容量不足时,会调用 grow 方法增长容量。

  1. /**
  2. * Appends the specified element to the end of this list.
  3. *
  4. * @param e element to be appended to this list
  5. * @return <tt>true</tt> (as specified by {@link Collection#add})
  6. */
  7. public boolean add(E e) {
  8. ensureCapacityInternal(size + 1); // Increments modCount!!
  9. elementData[size++] = e;
  10. return true;
  11. }
  12. private void ensureCapacityInternal(int minCapacity) {
  13. modCount++;
  14. // overflow-conscious code
  15. if (minCapacity - elementData.length > 0)
  16. grow(minCapacity);
  17. }
  18. private void grow(int minCapacity) {
  19. // overflow-conscious code
  20. int oldCapacity = elementData.length;
  21. int newCapacity = oldCapacity + (oldCapacity >> 1);
  22. if (newCapacity - minCapacity < 0)
  23. newCapacity = minCapacity;
  24. if (newCapacity - MAX_ARRAY_SIZE > 0)
  25. newCapacity = hugeCapacity(minCapacity);
  26. // minCapacity is usually close to size, so this is a win:
  27. elementData = Arrays.copyOf(elementData, newCapacity);
  28. }

3.add(int index, E element):在 index 位置插入 element。

  1. /**
  2. * Inserts the specified element at the specified position in this
  3. * list. Shifts the element currently at that position (if any) and
  4. * any subsequent elements to the right (adds one to their indices).
  5. *
  6. * @param index index at which the specified element is to be inserted
  7. * @param element element to be inserted
  8. * @throws IndexOutOfBoundsException {@inheritDoc}
  9. */
  10. public void add(int index, E element) {
  11. rangeCheckForAdd(index);
  12. ensureCapacityInternal(size + 1); // Increments modCount!!
  13. System.arraycopy(elementData, index, elementData, index + 1,
  14. size - index);
  15. elementData[index] = element;
  16. size++;
  17. }

4.addAll(Collection<? extends E> c) 和 addAll(int index, Collection<? extends E> c):将特定 Collection 中的元素添加到 Arraylist 末尾。

  1. /**
  2. * Appends all of the elements in the specified collection to the end of
  3. * this list, in the order that they are returned by the
  4. * specified collection's Iterator. The behavior of this operation is
  5. * undefined if the specified collection is modified while the operation
  6. * is in progress. (This implies that the behavior of this call is
  7. * undefined if the specified collection is this list, and this
  8. * list is nonempty.)
  9. *
  10. * @param c collection containing elements to be added to this list
  11. * @return <tt>true</tt> if this list changed as a result of the call
  12. * @throws NullPointerException if the specified collection is null
  13. */
  14. public boolean addAll(Collection<? extends E> c) {
  15. Object[] a = c.toArray();
  16. int numNew = a.length;
  17. ensureCapacityInternal(size + numNew); // Increments modCount
  18. System.arraycopy(a, 0, elementData, size, numNew);
  19. size += numNew;
  20. return numNew != 0;
  21. }
  22. /**
  23. * Inserts all of the elements in the specified collection into this
  24. * list, starting at the specified position. Shifts the element
  25. * currently at that position (if any) and any subsequent elements to
  26. * the right (increases their indices). The new elements will appear
  27. * in the list in the order that they are returned by the
  28. * specified collection's iterator.
  29. *
  30. * @param index index at which to insert the first element from the
  31. * specified collection
  32. * @param c collection containing elements to be added to this list
  33. * @return <tt>true</tt> if this list changed as a result of the call
  34. * @throws IndexOutOfBoundsException {@inheritDoc}
  35. * @throws NullPointerException if the specified collection is null
  36. */
  37. public boolean addAll(int index, Collection<? extends E> c) {
  38. rangeCheckForAdd(index);
  39. Object[] a = c.toArray();
  40. int numNew = a.length;
  41. ensureCapacityInternal(size + numNew); // Increments modCount
  42. int numMoved = size - index;
  43. if (numMoved > 0)
  44. System.arraycopy(elementData, index, elementData, index + numNew,
  45. numMoved);
  46. System.arraycopy(a, 0, elementData, index, numNew);
  47. size += numNew;
  48. return numNew != 0;
  49. }

在 ArrayList 的存储方法,其核心本质是在数组的某个位置将元素添加进入。但其中又会涉及到关于数组容量不够而增长等因素。

读取

这个方法就比较简单了,ArrayList 能够支持随机访问的原因也是很显然的,因为它内部的数据结构是数组,而数组本身就是支持随机访问。该方法首先会判断输入的index值是否越界,然后将数组的 index 位置的元素返回即可。

  1. /**
  2. * Returns the element at the specified position in this list.
  3. *
  4. * @param index index of the element to return
  5. * @return the element at the specified position in this list
  6. * @throws IndexOutOfBoundsException {@inheritDoc}
  7. */
  8. public E get(int index) {
  9. rangeCheck(index);
  10. return (E) elementData[index];
  11. }
  12. private void rangeCheck(int index) {
  13. if (index >= size)
  14. throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  15. }

删除

ArrayList 提供了根据下标或者指定对象两种方式的删除功能。需要注意的是该方法的返回值并不相同,如下:

  1. /**
  2. * Removes the element at the specified position in this list.
  3. * Shifts any subsequent elements to the left (subtracts one from their
  4. * indices).
  5. *
  6. * @param index the index of the element to be removed
  7. * @return the element that was removed from the list
  8. * @throws IndexOutOfBoundsException {@inheritDoc}
  9. */
  10. public E remove(int index) {
  11. rangeCheck(index);
  12. modCount++;
  13. E oldValue = elementData(index);
  14. int numMoved = size - index - 1;
  15. if (numMoved > 0)
  16. System.arraycopy(elementData, index+1, elementData, index,
  17. numMoved);
  18. elementData[--size] = null; // Let gc do its work
  19. return oldValue;
  20. }
  21. /**
  22. * Removes the first occurrence of the specified element from this list,
  23. * if it is present. If the list does not contain the element, it is
  24. * unchanged. More formally, removes the element with the lowest index
  25. * <tt>i</tt> such that
  26. * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
  27. * (if such an element exists). Returns <tt>true</tt> if this list
  28. * contained the specified element (or equivalently, if this list
  29. * changed as a result of the call).
  30. *
  31. * @param o element to be removed from this list, if present
  32. * @return <tt>true</tt> if this list contained the specified element
  33. */
  34. public boolean remove(Object o) {
  35. if (o == null) {
  36. for (int index = 0; index < size; index++)
  37. if (elementData[index] == null) {
  38. fastRemove(index);
  39. return true;
  40. }
  41. } else {
  42. for (int index = 0; index < size; index++)
  43. if (o.equals(elementData[index])) {
  44. fastRemove(index);
  45. return true;
  46. }
  47. }
  48. return false;
  49. }

注意:从数组中移除元素的操作,也会导致被移除的元素以后的所有元素的向左移动一个位置。

调整数组容量

从上面介绍的向 ArrayList 中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容有两个方法,其中开发者可以通过一个 public 的方法ensureCapacity(int minCapacity)来增加 ArrayList 的容量,而在存储元素等操作过程中,如果遇到容量不足,会调用priavte方法private void ensureCapacityInternal(int minCapacity)实现。

  1. public void ensureCapacity(int minCapacity) {
  2. if (minCapacity > 0)
  3. ensureCapacityInternal(minCapacity);
  4. }
  5. private void ensureCapacityInternal(int minCapacity) {
  6. modCount++;
  7. // overflow-conscious code
  8. if (minCapacity - elementData.length > 0)
  9. grow(minCapacity);
  10. }
  11. /**
  12. * Increases the capacity to ensure that it can hold at least the
  13. * number of elements specified by the minimum capacity argument.
  14. *
  15. * @param minCapacity the desired minimum capacity
  16. */
  17. private void grow(int minCapacity) {
  18. // overflow-conscious code
  19. int oldCapacity = elementData.length;
  20. int newCapacity = oldCapacity + (oldCapacity >> 1);
  21. if (newCapacity - minCapacity < 0)
  22. newCapacity = minCapacity;
  23. if (newCapacity - MAX_ARRAY_SIZE > 0)
  24. newCapacity = hugeCapacity(minCapacity);
  25. // minCapacity is usually close to size, so this is a win:
  26. elementData = Arrays.copyOf(elementData, newCapacity);
  27. }

从上述代码中可以看出,数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的 1.5 倍(从int newCapacity = oldCapacity + (oldCapacity >> 1)这行代码得出)。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造 ArrayList 实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity 方法来手动增加 ArrayList 实例的容量。

Fail-Fast 机制

ArrayList 也采用了快速失败的机制,通过记录 modCount 参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。 关于 Fail-Fast 的更详细的介绍,我在之前将 HashMap 中已经提到。

ArrayList实现原理的更多相关文章

  1. ArrayList实现原理(JDK1.8)

    ArrayList实现原理(JDK1.8) public class ArrayList<E> extends AbstractList<E> implements List& ...

  2. 【Java基础】ArrayList工作原理

    ArrayList 以数组实现.节约空间,但数组有容量限制.超出限制时会增加50%容量,用System.arraycopy()复制到新的数组.因此最好能给出数组大小的预估值.默认第一次插入元素时创建大 ...

  3. ArrayList实现原理分析

    ArrayList使用的存储的数据结构 ArrayList的初始化 ArrayList是如何动态增长 ArrayList如何实现元素的移除 ArrayList小结 ArrayList是我们经常使用的一 ...

  4. 透过源码分析ArrayList运作原理

    List接口的主要实现类ArrayList,是线程不安全的,执行效率高:底层基于Object[] elementData 实现,是一个动态数组,它的容量能动态增加和减少.可以通过元素下标访问对象,使用 ...

  5. Java中ArrayList实现原理

    简述: ArrayList可以理解为动态数组,与Java中的数组相比,它的容量能动态增长.超出限制时会增加50%容量,用System.arraycopy()复制到新的数组中,因此最好能给出数组大小的预 ...

  6. ArrayList实现原理及源码分析之JDK8

    转载 ArrayList源码分析 一.ArrayList介绍 Java 集合框架主要包括两种类型的容器: 一种是集合(Collection),存储一个元素集合. 一种是图(Map),存储键/值对映射. ...

  7. ArrayList底层原理

    ArrayList底层采用数组实现,访问特别快,它可以根据索引下标快速找到元素.但添加插入删除等写操作效率低,因为涉及到内存数据复制转移. ArrayList对象初始化时,无参数构造器默认容量为10, ...

  8. ArrayList 扩容原理

    面试中经常问到的问题之一就是List的扩容机制了,他是怎么做到扩容的,大家都能答出来底层是数组,复制一个数组来扩容,但是再具体一点来说,大家就不知道该怎么说了,如果不看源码说这么多确实就差不多了,但是 ...

  9. 4.Java集合-ArrayList实现原理及源码分析

    一.ArrayList概述: ArrayList 是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存 ArrayList不是线程安全的,只能用在单线程的情况 ...

随机推荐

  1. fighting_使用CSS美化文字

    CSS3颜色渐变 background-image:linear-gradient(black,blue,green,red); 默认从上到下显示. 示例代码: <!DOCTYPE html&g ...

  2. springday05-go1

    新建web工程spring-netcross1.导入spring文件夹里的七个jar包,另外还要导入jdbc-lib的四个jar包,ojdbc.jar,commoms-pool,commons-dbc ...

  3. CSS3 filter:drop-shadow滤镜与box-shadow区别应用 抄的

    CSS3 filter:drop-shadow滤镜与box-shadow区别应用 这篇文章发布于 2016年05月18日,星期三,01:07,归类于 css相关. 阅读 5777 次, 今日 12 次 ...

  4. react编译器jsxTransformer,babel

    1.JSX是什么JSX其实是JavaScript的扩展,React为了代码的可读性更方便地创建虚拟DOM等原因,加入了一些类似XML的语法的扩展. 2.编译器——jsxTransformerJSX代码 ...

  5. 不同版本mysql语句不兼容原因

    一般是sql_mode不相同,可以认为规则不一致.(语法的变化非常小,一般可以忽略) 如果想要导入不同版本的数据.可以: 手动处理一些导入错误或者采用其他的办法. 或者 修改sql_mode.具体修改 ...

  6. 夺命雷公狗---Thinkphp----16之首页的完成及全站的完成

    刚才我们首页只是完成了一部分的数据,那么这里我们就来将他所以的完成: IndexController.class.php控制器代码如下所示: <?php namespace Home\Contr ...

  7. 夺命雷公狗---DEDECMS----29dedecms热门大片的完成

    我们要完成的是热门大片的显示,如下所示: 热门大片,这一般都是按照浏览量高的放前面的而已,我们先来查查手册,如下所示: 这里发现我们的arclist标签里面的orderby和orderway完全可以实 ...

  8. PAT乙级 1001. 害死人不偿命的(3n+1)猜想 (15)

    1001. 害死人不偿命的(3n+1)猜想 (15) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 卡拉兹(Ca ...

  9. linux_c学习笔记之curl的使用一

    参考文档 使用libcurl发送PUT请求上传数据以及DELETE请求删除数据 http://blog.163.com/lixiangqiu_9202/blog/static/535750372014 ...

  10. java对redis的基本操作(转)

    本文转自:http://www.cnblogs.com/edisonfeng/p/3571870.html 2.主要类 1)功能类 package com.redis; import java.uti ...