【Java集合】List源码

1. LinkedList

LinkedList内部结构是双向链表,继承 AbstractSequentialList 类(继承自 AbstractList),除了实现List接口外还实现了Deque队列接口。它线程不安全

LinkedList成员变量以及Node节点的数据结构如下(阅读其源码可知,节点中的item元素可以为null):

public class LinkedList<E> ... {   
    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;
    }

它的UML类图如下:

源码:双向链表,感觉没啥好说的……

接下来讲下ArrayList吧

2. ArrayList
 

工作中用的最多的List。ArrayList 同样继承了AbstractSequentialList 类,本质上是一个数组,所以它支持随机访问。set/get时效率优于LinkedList;add/remove时输于LinkedList,因为ArrayList可能扩容、移位等。(数组与链表的区别,没啥好说的)它线程不安全

ArrayList成员变量如下:

public class ArrayList ... {

    private static final int DEFAULT_CAPACITY = 10;

    // 用于空实例的共享空数组实例
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 用于默认大小的空实例的共享空数组实例。我们将其与空元素数据区分开来,以便了解添加第一个元素时要膨胀多少。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    // 存储ArrayList元素的数组缓冲区。
    transient Object[] elementData;

    private int size;

}

它的UML图如下:

ArrayList的扩容操作:

    private 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);
    }

 不难看出,ArrayList每次扩容时扩容capacity数组容量的1.5倍。

3. Vector

Vector,向量也,底层也是数组,初始化容量与ArrayList都默认为10,同样支持随机访问,不同的是它是线程安全的,那为啥它是线程安全的呢?那是因为它主要的方法加了 synchronized 关键字(这种锁粒度太粗了,况且Vector还有数组扩容的耗时操作,所以不推荐为了线程安全性而使用Vector)。

它的UML图如下:

 它的成员变量如下:

    // 保存的数组
    protected Object[] elementData;

    // 元素个数
    protected int elementCount;
    
    // 容量自增的量。如果容量增量小于或等于零,则每次需要增长时,向量的容量都会翻倍。
    protected int capacityIncrement;

    // 要分配的最大数组大小。由于虚拟机在数组中保留一些头文字,尝试分配更大的数组可能会导致OOM:请求的数组大小超过VM限制
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造函数如下:

    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

扩容函数如下:

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }

如代码所说,Vector 每次扩容时都会增加capacityIncrement大小,不够则将数组扩大一倍(容易越界或造成空间浪费)。如果 新数组长度 < 原数组长度,不够的位置将被截断;相反的,多余的位置将被填充null。Vector扩容时,使用到 Arrays.copyOf() 数组复制操作,频繁扩容比较耗时。

4. Stack

Stack继承Vector,底层也是数组,使用数组尾部模拟栈顶。empty、peek、pop、push、search没啥好说的。 

5. 总结

LinkedList与ArrayList比较

       链表与数组的区别。写多,用LinkedList;读多,用ArrayList。

Vector与ArrayList比较

        对于扩容操作,ArrayList的扩容逻辑相对于数组容量是动态的,这保证了在扩容大小与扩容频率之间的平衡。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值