关于ArrayList的两个问题

最近在研究ArrayList,开始就发现了两个问题:

  1. ArrayList默认的初始容量大小?

  2. ArrayList的插入速度比LinkedList的慢?


背景:

  JDK 1.8


 

1. ArrayList默认的初始容量大小?

看源码

/**
  * Constructs an empty list with an initial capacity of ten.
  */
public ArrayList() {
  this.elementData = DEFAULTCAPACITY_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 = {}

第一张图片注释的意思是:

  Constructs an empty list with an initial capacity of ten.

  构造一个初始容量为10的空列表。

但是这里,默认构造方法只干了一件事,就是将elementData初始化为一个空数组。

那么,为什么说它的默认初始容量是10呢?

ArrayList有三个构造方法:

1. public ArrayList(int initialCapacity) {...}

2. public ArrayList() {...}

3. public ArrayList(Collection<? extends E> c) {...}

第三个暂且不说。第一个是指定ArrayList的初始化容量,第二个则是默认的构造方法。

如果不指定ArrayList的初始化容量,而使用默认构造方法去实例化ArrayList,则ArrayList会提供一个默认的容量大小,即为10。

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

但是,上面也说了 -- 默认构造方法只干了一件事,就是将elementData初始化为一个空数组。

那么,什么时候才会用到这个 DEFAULT_CAPACITY  常量呢?

答案是 -- 第一次添加元素时

执行add()方法最终会调用到这个grow()方法,也就是数组扩容的方法。

也就是说,第一次添加元素时,newCapacity的大小其实就是DEFAULT_CAPACITY (10)

 1 private void grow(int minCapacity) {
 2   // overflow-conscious code
 3     int oldCapacity = elementData.length;
 4     int newCapacity = oldCapacity + (oldCapacity >> 1);
 5     if (newCapacity - minCapacity < 0)
 6         newCapacity = minCapacity;
 7     if (newCapacity - MAX_ARRAY_SIZE > 0)
 8         newCapacity = hugeCapacity(minCapacity);
 9     // minCapacity is usually close to size, so this is a win:
10     elementData = Arrays.copyOf(elementData, newCapacity);
11 }

这个和JDK 1.6 的版本是不一样的。1.6 的版本会直接初始化一个数组容量为10的数组 。

还是看看源码吧。

JDK 1.6 的

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this(10);
}
/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param   initialCapacity   the initial capacity of the list
 * @exception IllegalArgumentException if the specified initial capacity
 *            is negative
 */
public ArrayList(int initialCapacity) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    this.elementData = new Object[initialCapacity];
}

 

JDK 1.7 的

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    super();
    this.elementData = EMPTY_ELEMENTDATA;
}
/**
 * Shared empty array instance used for empty instances.
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

小结:

  1. ArrayList的默认初始容量的确是10;

  2. 使用默认构造方法实例化ArrayList,只初始化了一个空数组;

  3. 第一次调用add()方法时,数组才会被扩容,数组大小为默认的初始容量--10(2是前提)

补充:

  我的考证仅限于1.6 到 1.8,其他版本可能还会有所不同


 

2. ArrayList的插入速度比LinkedList的慢?

add方法有两种:

(1)一种是直接在数组最后面插入

1 public boolean add(E e) {
2   ensureCapacityInternal(size + 1);  // Increments modCount!!
3     elementData[size++] = e;
4     return true;
5 }

 

(2)另一种是指定位置插入

1 public void add(int index, E element) {
2     rangeCheckForAdd(index);
3 
4     ensureCapacityInternal(size + 1);  // Increments modCount!!
5     System.arraycopy(elementData, index, elementData, index + 1, size - index);
6     elementData[index] = element;
7     size++;
8 }

 

说ArrayList的插入速度比LinkedList的慢是因为:

  ArrayList按序索引元素,所以插入新元素时涉及到其他已存在元素在数组中的前后移动,导致速度慢。

 

此刻,给一个条件,只在数组末尾插入元素,即仅使用add(E e)方法,不使用add(int index, E element)方法,那么结果会如何?

分析一下:

  只在末尾插入元素 == 不会导致中间已存在元素的变动。

也就是说:

  这个时候,使用ArrayList的插入速度应该更快。

 

当然,也不能胡说,来一个小实验验证一下: 

 1   public static void main(String[] args) {
 2         long before = System.currentTimeMillis();
 3         List list  = new ArrayList();
 4         //List list  = new LinkedList();
 5         for (int i = 0; i < 1000000; i++) {
 6             list.add(i);
 7         }
 8         long after = System.currentTimeMillis();
 9         System.out.println("插入一百万个元素,ArrayList需要时间:" + (after - before));
10         //System.out.println("插入一百万个元素,LinkedList需要时间:" + (after - before));
11     }

 

各重复5次,结果:

ArrayList:
  插入一百万个元素,ArrayList需要时间:28
  插入一百万个元素,ArrayList需要时间:32
  插入一百万个元素,ArrayList需要时间:63
  插入一百万个元素,ArrayList需要时间:31
  插入一百万个元素,ArrayList需要时间:32

LinkedList
  插入一百万个元素,LinkedList需要时间:132
  插入一百万个元素,LinkedList需要时间:128
   插入一百万个元素,LinkedList需要时间:133
   插入一百万个元素,LinkedList需要时间:149
   插入一百万个元素,LinkedList需要时间:149   

 

所以:

  应该改为 -- 随机插入元素时,ArrayList的速度比LinkedList的慢。

 

并且:

  删除元素的道理也是一样的,所以应该是 -- 随机增删元素时,ArrayList的速度比LinkedList的慢。

 


 

posted @ 2018-06-20 15:14  shadowdoor  阅读(564)  评论(0编辑  收藏  举报