Java学习笔记——容器之List

来源:互联网 发布:淘宝上论文发表靠谱吗 编辑:程序博客网 时间:2024/06/11 08:25

本文主要是分析实现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();        }    }


   


原创粉丝点击