jdk源码阅读笔记-ArrayList

一、ArrayList概述首先我们来说一下ArrayList是什么?它解决了什么问题?ArrayList其实是一个数组,但是有区别于一般的数组,它是一个可以动态改变大小的动态数组。ArrayList的关键特性也是这个动态的特性了,ArrayList的设计初衷就是为了解决Java数组长度不可变的问题。我们都知道在Java中数组一旦被创建出来,那么这个数组的大小就不可以改变了,而且初始化的时候就必须要...

jdk源码阅读笔记-ArrayList

  一、ArrayList概述

  首先我们来说一下ArrayList是什么?它解决了什么问题?ArrayList其实是一个数组,但是有区别于一般的数组,它是一个可以动态改变大小的动态数组。ArrayList的关键特性也是这个动态的特性了,ArrayList的设计初衷就是为了解决Java数组长度不可变的问题。我们都知道在Java中数组一旦被创建出来,那么这个数组的大小就不可以改变了,而且初始化的时候就必须要指定数组的大小。在开发的场景中很多时候我们并不知道我们的数据量有多少,如果数组创建得太大就会造成极大的空间资源的浪费,如果太小了又会报数组下标越界异常。这是我们在开发的过程中使用数组来存储数据时经常会遇到的问题,但是如果使用ArrayList的话,就可以轻易的解决这个问题,它能够根据你存放的数据的大小动态的改变数组的大小(本质创建新数组),所以我们在写代码的时候就不需要关心数组的大小了,我觉得这也是ArrayList创建出来所需要解决的问题。当然,如果在知道数组的大小且对数组的数据不增加的话,建议使用数组的存储数据,这样对性能的提升有一定的帮助。

  那么,ArrayList是怎么动态扩展数组大小的呢?下面通过源码一步一步揭开ArrayList的神秘面纱吧。

  

  二、ArrayList全局变量

  /**  * Default initial capacity.  */ //默认的初始化大小 private static final int DEFAULT_CAPACITY = 10; /**  * 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.  */ //这个是用来存放数据用的数组,add进来的数据都是放到这个数组里面的 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构造函数

  ArrayList的构造函数有三个:

  1、ArrayList(int initialCapacity):initialCapacity,数组的初始化大小,该构造器创建一个指定大小的空数组。

  2、ArrayList():创建一个默认大小为0的空数组

  3、ArrayList(Collection<? extends E> c):创建一个与c一样大小的数组,并将c的元素赋值到数组中。

**  * 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) {//创建一个 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) {  //将c转换成数组,toArray方法返回的数组类型有可能不是Object类型的  elementData = c.toArray();  if ((size = elementData.length) != 0) {// c.toArray might (incorrectly) not return Object[] (see 6260652)//数组类型不是Object类型,需要将类型转换成Object类,避免后面调用方法// 的时候出现类型转换异常if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class);  } else {// replace with empty array.this.elementData = EMPTY_ELEMENTDATA;  } }

  这里我们主要看一下Arrays.copyOf()方法是如何转换类型的:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {  @SuppressWarnings("unchecked")  T[] copy = ((Object)newType == (Object)Object[].class)? (T[]) new Object[newLength]: (T[]) Array.newInstance(newType.getComponentType(), newLength);  System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));  return copy; }

  这个方法中,如果传入的类型为Object类型,那么就直接创建一个Object数组,否则创建一个对应类型的数组。然后调用System.arraycopy方法,将原数组赋值到新创建的数组中,强调一下,凡是使用到数组的地方就一定会使用到arraycopy的方法,这个方法我在String源码阅读有说到过,在这里我再跟大家说一下这个方法吧。

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

  这是一个本地方法所以无法继续往下看源码了,但是从源码中可以知道每个参数代表的含义

  src:原数组

  srcPos:原数组中开始复制的位置

  dest:新数组,即目标数组

  destPos:目标数组存放的位置,即从原数组的复制过来的元素从这个位置开始放

  length:复制数组的长度

  举个代码示例:

public static void main(String[] args) {  int[] src = {1,2,3,4,5};  int[] dest = new int[6];  for (int i : dest) {System.out.print(i" ");  }  System.out.println();  System.arraycopy(src,0,dest,0,src.length);  for (int i : dest) {System.out.print(i" ");  } } //运行结果: //0 0 0 0 0 0  //1 2 3 4 5 0 

  看示例代码应该能够懂,第一次打印的时候dest只是被初始化没有赋值,所以里面每个元素存放的都是默认值,而int的默认值为0,所以打印出来的都是0,之后arraycopy后将src的所有数据复制到dest从0位置开始,所以打印的结果是 1 2 3 4 5 0

  四、add方法

/**  * Appends the specified element to the end of this list.  *  * @param e element to be appended to this list  * @return <tt>true</tt> (as specified by {@link Collection#add})  */ public boolean add(E e) {  //确认当前数组大小不会发生越界异常,否者对数组进行扩容  ensureCapacityInternal(size1);  // Increments modCount!!  e
源文地址:https://www.guoxiongfei.cn/cntech/3678.html