1. List接口及主要实现类特点
List:有序、可重复(“动态”数组);因而常常使用List替换数组,因为List 的容量是动态的。
-
ArrayList:底层使用Object[]存储
- 线程不安全,
- 追加(尾部添加)、查找效率高。时间复杂度O(1)。
- 删除、插入效率低。时间复杂度O(n)。
- 默认容量10,自动扩容1.5倍。
- 使用该类时,若数组容量确定尽量使用new ArrayList(int capacity)构造器,避免扩容操作。
-
LinkedList:底层使用双向链表存储,链表无容量问题,无需扩容。
- 插入、删除效率高。时间复杂度O(1)。
- 追加(尾部添加)、查找效率低。时间复杂度O(n)。
-
Vector:太古老,jkd1.0才使用,不介绍,
- 线程安全,效率低。
- 默认容量10,自动扩容2倍。
2. ArrayList源码解析
JDK 1.7.0_07
- 当我们们执行调用空参构造器时,底层会创建一个默认容量为10的数组。(类似单例模式—饿汉式)
- 当添加元素溢出时,底层会扩容。新建一个容量为原数组1.5倍的新数组,并将原数组元素复制过来。
//1.当我们们执行调用空参构造器时,底层会创建一个默认容量为10的数组
ArrayList<String> list = new ArrayList<>();//底层执行:Object[] elementData = new Object[10]
list.add("AA");//底层执行:elementData.[0] = "AA";
list.add("BB");//底层执行:elementData.[1] = "BB";
...
//2.当添加元素溢出时,底层会扩容。新建一个容量为原数组1.5倍的新数组,并将原数组元素复制过来。
// ArrayList扩容源码
private void grow(int minCapacity) {
//拿到原数组容量
int oldCapacity = elementData.length;
//新数组容量=原数组容量 + 原数组容量/2= 10 + 5(即扩容1.5倍)
int newCapacity = oldCapacity + (oldCapacity >> 1);
//...省略其他操作
// 复制元素
elementData = Arrays.copyOf(elementData, newCapacity);
}
JDK 1.8.0_271
- 当我们们执行调用空参构造器时,底层会创建一个数组,但是没有初始化容量。(类似单例模式—懒汉式)
- 当我们第一次添加元素时,才对数组进行初始化,容量为10。
- 当添加元素溢出时,底层会扩容。新建一个容量为原数组1.5倍的新数组,并将原数组元素复制过来。
//1.当我们们执行调用空参构造器时,底层会创建一个数组
ArrayList<String> list = new ArrayList<>();//底层执行:Object[] elementData = new Object[]{};
//2.第一次添加元素,会先初始化,在添加元素
list.add("AA");//底层执行:1. elementData = new Object[10]; 2. elementData.[0] = "AA";
list.add("BB");//底层执行:elementData.[1] = "BB";
...
//2.当添加元素溢出时,底层会扩容。新建一个容量为原数组1.5倍的新数组,并将原数组元素复制过来。
// ArrayList扩容源码
private void grow(int minCapacity) {
//拿到原数组容量
int oldCapacity = elementData.length;
//新数组容量=原数组容量 + 原数组容量/2= 10 + 5(即扩容1.5倍)
int newCapacity = oldCapacity + (oldCapacity >> 1);
//...省略其他操作
// 复制元素
elementData = Arrays.copyOf(elementData, newCapacity);
}
3. Vector源码解析(jdk 1.8.0_271)
- 当我们们执行调用空参构造器时,底层会创建一个默认容量为10的数组。(类似单例模式—饿汉式)
- 当添加元素溢出时,底层会扩容。新建一个容量为原数组2倍的新数组,并将原数组元素复制过来。
//1.当我们们执行调用空参构造器时,底层会创建一个默认容量为10的数组
Vector vector = new Vector<>();//底层执行:Object[] elementData = new Object[10]
vector .add("AA");//底层执行:elementData.[0] = "AA";
vector .add("BB");//底层执行:elementData.[1] = "BB";
...
//2.当添加元素溢出时,底层会扩容。新建一个容量为原数组1.5倍的新数组,并将原数组元素复制过来。
// Vector 扩容源码
private void grow(int minCapacity) {
// 原数组容量
int oldCapacity = elementData.length;
// 新数组容量 = 原数组容量 + 原数组容量 = 10 + 10(即扩容2倍),capacityIncrementz在每次添加元素前会自增1,用于记录数组长度,用于在添加元素前进行数组容量判断
// (capacityIncrement > 0) ? capacityIncrement : oldCapacity),大于10扩容,小于10就用原数组容量,保证容量一直为10
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
4.LinkedList源码解析(jdk 1.8)
LinkedList使用的是双向链表,因而不涉及容量问题,不需要初始化容量,也不需要扩容。
LinkedList<String> list = new LinkedList<>();// 底层空参构造器啥也没做
list.add("AA");//将“AA”封装到Node对象1中,list对象的first、last属性都指向这个Node对象
list.add("BB");//将“BB”封装到Node对象2中,Node对象1和Node对象2构成双向链表,对象1的last和对象2的first属性相互指向
LinkedList内部声明:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
add()操作源码:
/**
* 第1次add()调用:第一次添加元素调用
*/
private void linkFirst(E e) {
final Node<E> f = first;//这是第一个元素
final Node<E> newNode = new Node<>(null, e, f);//头指针为null,尾指针指向第一个元素
first = newNode;//头指针指向自己
if (f == null)
last = newNode;//将自己作为下一个元素的头指针,参考第2次调用final Node<E> l = last;
else
f.prev = newNode;//上一个元素的头指针指向自己
size++;
modCount++;
}
/**
* 第1次之后add()调用:
*/
void linkLast(E e) {
final Node<E> l = last;// 这是上一个元素
final Node<E> newNode = new Node<>(l, e, null);//头指针指向上一个元素,尾指针为null
last = newNode;//将自己作为下一个元素头指针,参考前2步
if (l == null)
first = newNode;//若没有上一个元素,则自己就是第一个元素
else
l.next = newNode; //将上一个元素的尾指针指向自己
size++;
modCount++;
}
5. List常用方法
系列一
Collection中的方法List都可以使用,因为List是Collection的子接口
系列二
因为List是有序的,进而就有了索引,所以就会增加一些多索引操作的方法:
插入元素
- void add(int index, Object ele):在index位置插入ele元素
- boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
获取元素
- Object get(int index):获取指定index位置的元素
- List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
获取元素索引
- int indexOf(Object obj):返回obj在集合中首次出现的位置
- int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
删除和替换元素
- Object remove(int index):移除指定index位置的元素,并返回此元素
- Object set(int index, Object ele):设置指定index位置的元素为ele