Java学习笔记——容器之List

本文主要是分析实现List接口的ArrayList、LinkedList、Vector、Stack的源码。


1. ArrayList

     ArrayList是使用Object数组保存数据的,并且加上了transient关键字,所以在ArrayList序列化时,elementData不会直接被序列化

    transient Object[] elementData; 
    ArrayList有3种构造函数:第1种是无参的,这时候 elementData是一个长度为0的空数组, 当往List里添加元素时才会为elementData分配空间;第2种是传入 initialCapacity 的参数,会为 elementData分配 initialCapacity的空间;第3种是传入List,此时会用

Arrays.copyOf拷贝传入list中的数据给elementData。

  ArrayList中数组采用1.5倍的增长方式的,如果增长后空间还不够,则数组长度取当前要插入数据总长度,增长后将原来的数据拷贝到新生成的数组中,代码如下:

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

2.LinkedList

    LinkedList是采用链表存储数据的List,有以下三个成员变量,分别表示长度、链表头、链表尾。

    transient int size = 0;

    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;
        }
    }
       LinkedList除了支持List接口的方法外,还支持peek、poll、push、pop等方法,只要对于数据结构有一定了解的同学都能够完成类似代码的编写,因此在此就不做过多的介绍。 另外,在LinkedList的remove和Clear方法中,都有将node的item、next、prev置为null,以便GC回收。

    public void clear() {
        // Clearing all of the links between nodes is "unnecessary", but:
        // - helps a generational GC if the discarded nodes inhabit
        //   more than one generation
        // - is sure to free memory even if there is a reachable Iterator
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }

3.Vector

     vector同ArrayList一样,都是采用数组来存储数据,但vector与ArrayList有两点不同:

    (1)Vector在public的方法上使用了 synchronized 关键字来保证同步

 (2)Vector在构造时可以传递一个长度的增长量capacityIncrement,如果该值不小于0则每次按该值增长,否则按2倍增长。

4.Stack

    Stack继承自Vector,只不过多了peek、push、pop三个方法。


      上述的ArrayList、LinkedList、Vector、Stack,ArrayList和LinkedList都是非线程安全的,因此要使用线程安全的List,可以调用Collections.synchronizedList方法,它会将传入的List包装成SynchronizedList,是线程安全的,代码如下:

    public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }

     SynchronizedList继承自SynchronizedCollection,它有一个成员变量mutex作为同步的信号量,在实现的List的每一个方法中,采用synchronized关键字获取mutex,然后再调用包装前的list相应方法。


5.CopyOnWriteArrayList

  CopyOnWriteArrayList也是采用数组来存储数据的,但是它每次新增数据时都会重新生成一个新的数组,因此CopyOnWriteArrayList适用于读多写少的场景。与上述几个类不同,它位于concurrent包,通过ReentrantLock类型的成员变量lock实现线程安全,在add、remove、retain、set等方法对数据进行操作之前,先调用ReentrantLock的lock()方法获取锁,操作完成之后再调unlock释放锁。如Add方法的源码如下所示:

 public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }


   


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值