Java高级技术第四章——Java容器Collection之List详解

前言

前言点击此处查看:
http://blog.csdn.net/wang7807564/article/details/79113195

Collection

List

List是一种有序的Collection,使用此接口能够精确地控制每个元素插入的位置。用户能够使用索引来访问List中的元素,每个元素的索引是固定的,我们可以认为List是一种动态的数组。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack.

ArrayList

ArrayList的底层实现原理是开辟出一块数组区域,能够加入任何类型的元素,包括null.ArrayList的size(),isEmpty(),get(),set()方法运行时间为常数。但是使用add()方法添加n个元素需要O(n)的时间,其他的方法运行时间为线性。
也就是说,使用ArrayList是要求随机读取的场景,因为Array在随机读取的时候效率很高,但是在频繁插入和删除的场景中,效率就会相对差一些。
ArrayList的部分核心JDK源码是这样的:

//增加
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,size - index);
        elementData[index] = element;
        size++;
    }

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
//删除
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

//查询
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
//修改
    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

其中,elementData是一个Object类型的数组,用于存储元素。当添加元素的时候,需要先判断elementData数组的长度,然后为其扩容,扩容的核心函数是grow(),其源代码是:

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

LinkedList

LinkedList与ArrayList类似,都类似于动态数组。与ArrayList的区别在于,LinkedList的底层数据结构是链表,这使得其在增加和删除某个元素的时候速度很快,是一个常数的复杂度,但是,在随机读取某个元素的时候,效率就会比较差,因为游标需要移动。所以,在大量增加和删除元素的时候,我们可以使用LinkedList,需要随机读取元素的时候,选用ArrayList.
此外LinkedList提供额外的get(),remove(),insert()方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
它的JDK实现源代码是:

    transient int size = 0;
    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;
    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
//链表的数据结构
    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 void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    /**
     * Links e as first element.
     */
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

    /**
     * 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++;
    }
//删除
        public void remove() {
            checkForComodification();
            if (lastReturned == null)
                throw new IllegalStateException();

            Node<E> lastNext = lastReturned.next;
            unlink(lastReturned);
            if (next == lastReturned)
                next = lastNext;
            else
                nextIndex--;
            lastReturned = null;
            expectedModCount++;
        }
//查询
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
//修改
    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

Vector & Stack

Vector与ArrayList很类似,区别是Vector的方法是同步的,这些方法被加上synchronized关键字修饰,这将在后面的高并发中具体阐述。
由Vector创建的Iterator,虽然和ArrayList创建的Iterator实现的同一接口,但是,由于Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(如添加或删除了一些元素),这时调用Iterator的方法时将抛出 ConcurrentModificationException异常,需要手动去捕获该异常。
Stack类继承自Vector,实现的是一个堆栈的数据结构。Stack提供5个额外的方法使得Vector可以被当作堆栈使用。这五个额外的方法分别是:push()和pop()方法入栈和出栈,peek()方法用于得到栈顶的元素,empty()方法测试堆栈是否为空,search()方法检测某元素在堆栈中的位置。Stack刚创建后是元素是空的。
Stack继承自Vector,自然也是由synchronized修饰过的同步容器。而除了这两个类,其他实现List接口的容器类并没有实现同步,在多线程场景下需要注意线程安全问题。有关同步容器,将在后面的高并发中具体涉及到。

猜你喜欢

转载自blog.csdn.net/wang7807564/article/details/79636285