解析List集合子类ArrayList、Vector、LinkedList底层及扩容机制
ArrayList
特点
- 高效率
- 数组
- 查询和修改快
- 不安全
有序
扩容机制
1.使用无参构造器初始化时,会创建一个elementData的空数组,没有初始容量.
/** * DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为一个空数组 * private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
2.当调用集合第一次进行add操作时,会进行当前数组elementData是否是空判断,如果为空,则形成一个最小所需容量为10的最小长度,随后则进行扩容,如果根据所需容量计算的结果小于0,则进行新容量的反赋值操作,随后则调用Arrays.copyOf方法进行数组的赋值,并且完成赋值。
3.当所需容量大于10的时候,则按照现有容量的1.5倍进行扩容,并完成赋值。
4.使用有参构造时则使用有参构造初始化elementData数组,当容量不够时,同上3private 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); }
Vector集合
特点
- 低效率
- 数组
- 安全(synchronized悲观锁)
有序
扩容机制
1.使用无参构造初始化时,默认容量为10.
/** * Constructs an empty vector so that its internal data array * has size {@code 10} and its standard capacity increment is * zero. */ public Vector() { this(10); }
2.当所需容量小于现有容量时,会按照现有容量的2倍进行扩容
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // 因为capacityIncrement默认为0,所以扩容等同于:现有容量+现有容量(2倍) 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); }
3.调用Arrays.copyOf方法进行数组的复制,并完成赋值。
LinkedList
特点
- 增加和删除快
- 底层非数组,而是一个Node对象[prev(上一个元素)、item(存储)、next(下一个元素))]
- 不安全
双向链表
解析
1.首先要知道linkedList的一个内部类Node对象,这个对象有三个属性,分别是item:要存储的值,next:下一个元素,prev:上一个元素。
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; } }
1.1可以自己创建一个Node节点对象,玩一下node节点的如何连接。
class Node<T> { public T obj; public Node<T> next; public Node<T> pre; public Node(T obj) { this.obj = obj; } @Override public String toString() { return "Node{" + "obj=" + obj + '}'; } }
1.2测试代码,创建三个node节点,将每个节点相连接,形成一种链式结构。
Node<String> XM = new Node<>("小明"); Node<String> ZS = new Node<>("张三"); Node<String> LS = new Node<>("李四"); // 让每个node节点的下一个节点指向下一个对象 XM.next = ZS; ZS.next = LS; // 每个节点的上一个指向 LS.pre = ZS; ZS.pre = XM; Node<String> first = new Node<>("First"); Node<String> last = new Node<>("Last"); first = XM; // 根据首元素遍历 while (true) { if (first == null) { break; } System.out.println(first); first = first.next; } System.out.println("==================pre================="); last = LS; while (true) { if (last == null) { break; } System.out.println(last); last = last.pre; } // 讲小红插入张三和李四之间 /** * 1.将张三的下一个元素指向小红 * 2.将小红的下一个元素指向李四 * 3.将小红的上一个元素指向张三 * 4.将李四的上一个元素指向小红 */ Node<String> XH = new Node<>("小红"); ZS.next = XH; XH.next = LS; XH.pre = ZS; LS.pre = XH; System.out.println("==========小红插入后============="); first = XM; // 根据首元素遍历 while (true) { if (first == null) { break; } System.out.println(first); first = first.next; } System.out.println("==================pre================="); last = LS; while (true) { if (last == null) { break; } System.out.println(last); last = last.pre; }
2.linkedList集合初始化后会创建一个空集合
此时内部的三个属性,size为0,first和last属性为null。/** * Constructs an empty list. */ public LinkedList() { }
3.当集合进行第一次add操作时,真正做的是将最后一个元素和新添加的元素相连接,此时集合的last属性为null,则元素e既是first又是last元素。
/** * Links e as last element. */ void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
4.当集合第二次进行add操作时,会直接将last属性作为新元素的上一个元素,此时新元素则为集合的last元素,此时的l不为空,则将l的下一个元素指向新元素。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。