一、容器图谱
二、Iterator 和 Iterable
Iterator
是一个接口,该接口通常会以内部类的形式实现,可以使用 Iterator 对象遍历Collection
(集合):
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("H", "E", "L", "L", "O");
Iterator<String> iter = list.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
}
}
Collection 接口继承了Iterable
接口,所以可以使用 Iterable 接口中的iterator()
方法获取 Iterator 对象。
考虑使用for each 循环
遍历集合:
for(String str : list) {
System.out.println(str); }
反编译字节码:
29: invokestatic #17 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
32: astore_1
33: aload_1
34: invokeinterface #23, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
39: astore_2
40: aload_2
41: invokeinterface #29, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
46: ifeq 69
49: aload_2
50: invokeinterface #35, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
55: checkcast #7 // class java/lang/String
58: astore_3
59: getstatic #39 // Field java/lang/System.out:Ljava/io/PrintStream;
62: aload_3
63: invokevirtual #45 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
66: goto 40
69: return
发现其实使用的就是 Iterator 遍历的集合~
Iterator 接口还有一个remove
方法:
- 如果迭代器不支持删除操作,则该方法会抛出 UnsupportedOperationException 异常。(默认实现)
- 如果迭代器支持删除操作,则该方法会删除上次调用 next 方法时返回的元素。如果调用 remove 方法之前没有调用 next 方法,或者连续调用了两次 remove 方法,将会抛出一个 IllegalStateException 异常。
三、Collection
1. List
List 是一个有序集合,可以存储重复的元素。
1.1 ArrayList
ArrayList 是支持动态扩容的数组,它的底层是一个 Object 类型的数组,所以可以存放任何类型的元素。
① 构造对象
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
private static final Object[] EMPTY_ELEMENTDATA = {
}; // 共享的容量为0的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
}; // 共享的标记为默认容量的空数组
transient Object[] elementData; // 数组缓冲区,存储数组元素
public ArrayList() {
// 引用标记为默认容量的空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 使用指定的容量创建数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 引用容量为0的空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 抛出异常
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
}
解析:
- 构造对象时如果没有指定容量,则使用默认的初始容量,但是这时并不会真正创建一个容量为 10 的数组,而是引用一个共享的标记为默认容量的空数组。注意此时实际的容量还是
0
,添加元素时需要进行扩容。 - 构造对象时如果指定的容量大于 0,则创建指定容量的数组;如果等于 0,则引用一个共享的容量为 0 的空数组;如果小于 0,则抛出异常。
② 添加元素
protected transient int modCount = 0; // AbstractList 中的字段,表示列表修改的次数(fail-fast 迭代器)
private int size; // 数组列表包含的元素数量
transient Object[] elementData; // 数组缓冲区,存储数组元素
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
// 如果元素数量等于数组缓冲区的容量则扩容
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
// 将元素插入到指定位置
public void add(int index, E element) {
// 检查index是否合法
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
// 移动元素
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
③ 删除元素
private int size; // 数组列表包含的元素数量
transient Object[] elementData; // 数组缓冲区,存储数组元素
// 删除指定位置的元素,并返回该元素
public E remove(int index) {
// 检查index是否合法
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
// 如果删除的不是尾部的元素,则进行移动
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
// 重置尾部元素的值
es[size = newSize] = null;
}
④ 自动扩容
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
}; // 默认容量的空数组
private int size; // 数组列表包含的元素数量
transient Object[] elementData; // 数组缓冲区,存储数组元素
private Object[] grow() {
// 至少扩容为size+1
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
// 数组缓冲区的容量
int oldCapacity = elementData.length;
// 如果当前容量大于0或者数组不为空
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 计算新数组的容量
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity,
oldCapacity >> 1);
// 浅拷贝数组,并使其容量为newCapacity
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
// 创建容量至少为10的数组
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// 如果元素数量大于原来容量的1.5倍,则将容量变为元素数量,否则变为原来的1.5倍
int prefLength = oldLength + Math.max(minGrowth, prefGrowth);
// 如果计算结果没有溢出,则返回结果
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
return prefLength;
} else {
// 如果计算结果溢出,调用hugeLength方法
return hugeLength(oldLength, minGrowth);
}
}
// 保守的最大数组长度
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
private static int hugeLength(int oldLength, int minGrowth) {
// 至少需要的容量
int minLength = oldLength + minGrowth;
if (minLength < 0) {
// 如果结果溢出,则抛出异常
throw new OutOfMemoryError(
"Required array length " + oldLength + " + " + minGrowth + " is too large");
} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
// 如果没有超过保守的最大数组长度,则返回保守的最大数组长度
return SOFT_MAX_ARRAY_LENGTH;
} else {
// 如果超过了保守的最大数组长度,但是没有溢出,则返回该值
return minLength;
}
}
⑤ 手动扩容
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
}; // 共享的标记为默认容量的空数组
transient Object[] elementData; // 数组缓冲区,存储数组元素
public void ensureCapacity(int minCapacity) {
// 要求指定容量大于当前容量,并且如果指定容量小于默认容量,则当前数组列表不能是用无参构造新创建的
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
解析:如果已知数组列表需要添加大量元素,可以提前手动扩容避免频繁的扩容和浅拷贝操作。
⑥ 缩减容量
private static final Object[] EMPTY_ELEMENTDATA = {
}; // 共享的容量为0的空数组
private int size; // 数组列表包含的元素数量
transient Object[] elementData; // 数组缓冲区,存储数组元素
public void trimToSize() {
modCount++;
// 如果元素数量小于当前容量
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
解析:添加元素时数组会自动扩容,但是删除元素不会缩减容量,所以在需要时我们可以调用该方法手动进行缩容。
1.2 LinkedList
LinkedList 的底层是一个双向链表,它实现了 List 和 Deque 接口。
① 构造对象
transient int size = 0; // 链表的元素数量
transient Node<E> first; // 指向头结点的指针
transient Node<E> last; // 指向尾结点的指针
// 结点内部类
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {