容器脑图
List 常见的实现类
- ArrayList
注:在dubug ArrayList的时候需要main方法中随意打个断点,不要先在内部方法打断点
//一些常用的变量及方法
//创建一个默认为空的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存放元素的数组缓冲区
transient Object[] elementData;
//数组的Size
private int size;
//默认扩容10
private static final int DEFAULT_CAPACITY = 10;
//用来记录数组操作次数
protected transient int modCount = 0;
//添加元素
public boolean add(E e) {
//(1)ensureCapacityInternal确保容量足够
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//(1)ensureCapacityInternal确保容量足够
private void ensureCapacityInternal(int minCapacity) {
//(2)calculateCapacity计算容量 minCapacity最小容量
//(3)ensureExplicitCapacity确保容量足够
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//(2)计算返回数组初始化大小 minCapacity最小容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
(3)ensureExplicitCapacity确保容量足够
private void ensureExplicitCapacity(int minCapacity) {
//记录操作次数
modCount++;
// overflow-conscious code
//(4)grow给数组扩容方法
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//(4)grow给数组扩容方法
private void grow(int minCapacity) {
// 获取elementData缓冲区原长度oldCapacity
int oldCapacity = elementData.length;
// 创建一个新的缓冲区长度newCapacity
// X>>n 的意思是 取 x/2^n 取这个数除以2的n次方
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);
}
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
//(5)给数组elementData扩容
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
//判断泛型是否为object的子类 返回true则copy等于创建一个容量为10的空数组
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
//(5)数组复制(native是调用jvm底层c或c++实现)
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
//最后返回到add方法将elementData的size++赋值为E返回True
(1)ensureCapacityInternal 是为了在添加元素的时候确保数组的容量足够
(2)计算返回数组初始化大小 用默认扩容大小DEFAULT_CAPACITY 10 和elementData的size++比较获取 minCapacity最小容量
(3)ensureExplicitCapacity 用(2)方法返回的最小扩容大小和现在的和elementData的size比较确保容量足够
(4)在创建一个空的集合后,虽然默认扩容为10,但是是在第一个元素添加时,用集合的size+1得到最小容量最后取最小容量和默认容量中较大的值来给数组扩容。
(5)具体原理点我查看
总结:
- ArrayList的底层实现是动态数组,为什么说是动态数组呢?因为元素的添加删除都需要创建一个新的数组去接收最后的结果。
- ArrayList并没有加锁,所以在多线程操作的时候会产生数据同步问题,所以是线程不安全的。
- ArrayList是有序可重复的是因为底层使用数组实现的。
2.LinkedList
LinkedList是双向链表实现的,理解其原理更多的是理解链表的的原理
//首节点
transient Node<E> first;
//尾节点
transient Node<E> last;
//内部定义的Node
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;
}
}
//添加元素
public boolean add(E e) {
linkLast(e);
return true;
}
//链接元素作为最后一个Node
void linkLast(E e) {
//获取记录的最后一个Node
final Node<E> l = last;
//新建一个Node 将要添加的元素及刚获取的最后一个Node作为新Node的Prev,Next为空
final Node<E> newNode = new Node<>(l, e, null);
//将原先的Last存放的Node替换成现在的Node
last = newNode;
//原先最后一个Node为空说明该链表也为空还未添加过元素
if (l == null)
//则将第一个设置首Node
first = newNode;
else
//否则设置原先尾Node的Next为现在添加的Node
l.next = newNode;
size++;
modCount++;
}
//删除方法
public E remove(int index) {
//校验索引是否越界
checkElementIndex(index);
return unlink(node(index));
}
//获取index位置的节点Node
Node<E> node(int index) {
// assert isElementIndex(index);
//获取链表长度的1/2,判断index在哪儿范围
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
//解除要删除的Node与其前后Node的链接
E unlink(Node<E> x) {
// assert x != null;
//获取被删除的值
final E element = x.item;
//获取被删除节点的下一个节点
final Node<E> next = x.next;
//获取被删除节点的下一个节点
final Node<E> prev = x.prev;
//判断上一个节点是否为空,如果为空说明这个被删除的为first节点,则将他的下一个节点设置为first节点,否则将被删除节点的next节点设置到前一个节点上。
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
//判断下一个节点是否为空,如果为空说明该删除节点是尾节点,则将尾节点重新设置为被删除节点的前一个节点,否则将删除节点的prev节点设置到nex节点上
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
//此处和上面的x.next=null都是为了方便gc的垃圾回收
x.item = null;
size--;
modCount++;
return element;
}
总结:
- LinkedList的增删操作都是对节点的操作。
- LinkedList也是线程不安全的并没有加同步锁
- 其余方法就不一一列举,可以自己调试理解
3.Vector
Vector和ArrayList实现方式一样都是使用的动态数组,只是在操作时加了Synchronized来保证多线程操作时的数据安全问题。
其余几种实现类
- CopyOnWriteArrayList
顾名思义写时复制,该实现类也保证了 线程安全使用的是ReentrantLock 自旋锁,相比Vector的Synchronized重量级锁来说性能更优。