Java源码之各种List
在C中,我们通常会用链表,用数组来处理顺序的结构,Java中,为了方便,通常会选用List来完成各种操作
Java中常见的List有:ArrayList,LinkedList(参见Java.util.各种List)
ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ......................... }
LinkedList
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { ...................... }
他们的共同之处在于实现了List<E>接口。同时,继承的类也并不相同,一个是最基础的AbstractList<E>,一个是AbstractSequentialList<E>,这两个抽象类是后者继承前者。
看ArrayList和LinkedList这两个类,名字其实很清楚的说明了,什么时候用前者,什么时候用后者
ArrayList:动态的数组结构,可动态的扩展数组的大小
LinkedList:链表结构,Java的链表实现
ArrayList默认初始化的大小是10,在增长的过程中有可能会重新分配一段更大的内存将之前的数组内容复制过来,但是因为不是每次增长都会进行复制(太低效),因此在实现的过程中,会先分配一个更大的数组,来进行存储。
具体的过程如下:add时,首先检查存储大小调用 ensureCapacityInternal
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
在调用ensureCapacityInternal的时候如果这时候内存不够,就会进行数组增长,调用grow方法
private void ensureCapacityInternal(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
grow方法就会为数组分配一段更大的内存,然后将之前的内容复制过来
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
于是乎很明显,ArrayList的实现是有内存浪费的。
因为是顺序存储,get某一位置的元素类似于数组的访问方式,因此有着高效率的随机存取。
LinkedList以一段代码为例:
1 private void linkFirst(E e) { 2 final Node<E> f = first; 3 final Node<E> newNode = new Node<>(null, e, f); 4 first = newNode; 5 if (f == null) 6 last = newNode; 7 else 8 f.prev = newNode; 9 size++; 10 modCount++; 11 }
很明显,操作方式与链表类似,支持前插后插,故而很显然,LinkedList不支持高效的随机存取。
所以就像C或者C++中的链表和数组一样,在不同的情况下选择使用ArrayList或者LinkedList,同时ArrayList的动态性是牺牲掉一定的复制过程的开销来满足的。