关于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的慢。