List源码比较简单,里面就是定义了一些方法。
List源码600行
读取(get),插入(add),删除(remove),修改(set),也可批量增加(addAll),删除(removeAll,retainAll),获取(subList)。
还有一些判定操作:包含(contains[All]),相等(equals),索引(indexOf,lastIndexOf),大小(size)。还有获取元素类型数组的操作:toArray()
ArrayList源码1172行
ArrayList是基于数组实现的,是一个动态数组(长度可变的数据结构,允许null当元素,默认长度是10,一次增长0.5倍,其容量为原来的1.5倍。
ArrayList提供了三种方式的构造器:
1、可以构造一个默认初始容量为10的空列表;
2、构造一个指定初始容量;
3、构造一个包含指定collection的元素的列表。
// ArrayList带容量大小的构造函数。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
// 新建一个指定容量的数组</span>
this.elementData = new Object[initialCapacity];
}
// ArrayList无参构造函数。默认容量是10。
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
//构造方法传入一个Collection, 则将Collection里面的值copy到arrayList
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
调整数组容量:
ensureCapacity(intminCapacity): 从上面介绍的向ArrayList中 存储元素的代码中,我们看到, 每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数 据的需求。数组扩容通过一个公开的方法ensureCapacity(intminCapacity)来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。另外扩容一次其容量为原来的1.5倍。
public void ensureCapacity(int minCapacity) {
if (minCapacity > 0)
ensureCapacityInternal(minCapacity);
}
问题:
数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。 所以在初始化ArrayList的时候尽量预算下大致的容量需求,降低频繁调整容量的开销。void trimToSize() : 将底层数组的容量调整为当前列表保存的实际元素的大小的功能。
由于elementData的长度会被拓展,size标记的是其中包含的元素的个数。所以会出现size很小但 elementData.length很大的情况,将出现空间的浪费。trimToSize将返回一个新的数组给elementData,元素内容保持不 变,length和size相同,节省空间。
public void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
}
LinkedList源码1138行
LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。链表主要包含单向链表,单向循环链表,双向链表,双向循环链表。LinkedList是属于双向链表,下图是包含头结点和尾节点的双向链表。
LinkedList底层的数据结构是基于双向链表的,且头结点中不存放数据,如下:
既然是双向链表,那么必定存在一种数据结构——我们可以称之为节点,节点实例保存业务数据,前一个节点的位置信息和后一个节点位置信息,如下图所示:
元素添加步骤:
第一步:调用无参构造方法,创建LinkedList实例
第二步:初始化一个预添加的Entry实例(newEntry)。
第三步:调整新加入节点和头结点(header)的前后指针。
LinkedList提供了两个构造方法:
public LinkedList() {
header.next = header.previous = header;
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
删除数据remove()
由于删除了某一节点因此调整相应节点的前后指针信息,如下:
e.previous.next = e.next;//预删除节点的前一节点的后指针指向预删除节点的后一个节点。
e.next.previous = e.previous;//预删除节点的后一节点的前指针指向预删除节点的前一个节点。
Vector源码1212行
Vector:ArrayList的前身,数组结构,线程安全,速度慢
Vector构造函数。默认容量是10。一次增长原来的一倍。
public Vector() {
this(10);
}
其他分析,请参考ArrayList