java集合浅谈——List

一、类库结构图概览

Java中集合类库的结构图,如下所示:

图1

图2

二、Collection接口说明

(1)Collection是最基本的集合接口,由Collection接口派生的两个接口是List和Set。

(2)所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数和有一个Collection类型参数的构造函数。前者用于创建一个空的Collection,后者用于创建一个新的Collection,允许用户复制一个Collection。

(3)不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,可逐一访问Collection中每一个元素。用法如下:

Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
    Object obj = it.next(); // 得到下一个元素
}

 

Note:

容器对象仅能持有对象引用(对象的指针),而不是Copy对象信息 

三、List源码解读

List以某种插入顺序来维护元素顺序,另外元素可以重复。

ArrayList

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList是用数组(Object类型的数组,被标注为transient)来实现的,读取速度快,插入与删除速度慢(因为插入与删除时要移动后面的元素),适合于随机访问。

ArrayList是动态增长的,并确保始终存在添加元素的空间。

初始化ArrayList时可以指定容量,源代码如下:

    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

通过add(E e)方法添加元素时,可能需要调整内部数组的大小,代码如下:

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    /**
     * This helper method split out from add(E) to keep method
     * bytecode size under 35 (the -XX:MaxInlineSize default value),
     * which helps when add(E) is called in a C1-compiled loop.
     */
    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

 

问题:How does the remove method work in ArrayList?  [ArrayList shrinks automatically ]

可以使用remove(int i) 或remove(Object o)从ArrayList中删除一个元素。

当移除任何元素时,内部所有后续元素都要左移,以填补被移除元素的空白,数组的大小将减 1。

    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        Objects.checkIndex(index, size);
        final Object[] es = elementData;

        @SuppressWarnings("unchecked") E oldValue = (E) es[index];
        fastRemove(es, index);

        return oldValue;
    }
    /**
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(Object[] es, int i) {
        modCount++;
        final int newSize;
        if ((newSize = size - 1) > i)
            System.arraycopy(es, i + 1, es, i, newSize - i);
        es[size = newSize] = null;
    }

 

扩展阅读:

Array vs ArrayList in Java

LinkedList

是用双向链表来实现的,删除与插入速度快,读取速度较慢,因为它读取时是从头向尾或从尾向头查找元素,适合于元素的插入与删除操作。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

 

问题:How add() method works in a LinkedList

    /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    /**
     * 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++;
    }

内部数据结构是双向链表,示意图如下:

 

 对应源代码如下:

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

问题:How remove() method works in Java LinkedList class

 当调用remove()方法时,必须更改删除节点左侧和右侧节点的引用,源代码如下:

    /**
     * Removes the element at the specified position in this list.  Shifts any
     * subsequent elements to the left (subtracts one from their indices).
     * Returns the element that was removed from the list.
     *
     * @param index the index of the element to be removed
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    /**
     * Unlinks non-null node x.
     */
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

 

Vector

功能与ArrayList几乎相同,也是用数组来实现的,其添加、删除等都是基于线程同步的,源代码如下:

    /**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        add(e, elementData, elementCount);
        return true;
    }

初始化Vector时可以设定容量,源代码如下:

    /**
     * Constructs an empty vector with the specified initial capacity and
     * with its capacity increment equal to zero.
     *
     * @param   initialCapacity   the initial capacity of the vector
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

如果以new Vector()方式创建Vector,则其初始容量为10;

如果以new Vector(int initialCapacity, int capacityIncrement)方式创建Vector,则其初始容量为initialCapacity。

Stack

Stack继承自Vector,实现一个后进先出的堆栈,线程同步。

除了基本的入栈和出栈操作,该类还提供了另外几个函数,如:peek、search,对应源代码如下:

    /**
     * Looks at the object at the top of this stack without removing it
     * from the stack.
     *
     * @return  the object at the top of this stack (the last item
     *          of the {@code Vector} object).
     * @throws  EmptyStackException  if this stack is empty.
     */
    public synchronized E peek() {
        int     len = size();

        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
    }

 

Note:

Stack是继承自Java中的Vector的遗留类,当我们不需要线程安全时会有额外的开销。

建议使用ArrayDeque来实现堆栈,因为它在单线程环境中效率更高。

posted @ 2015-07-31 10:25  时空穿越者  阅读(405)  评论(0编辑  收藏  举报