zoukankan      html  css  js  c++  java
  • Java容器List接口

    List接口是Java中经常用到的接口,如果对具体的List实现类的特性不了解的话,可能会导致程序性能的下降,下面从原理上简单的介绍List的具体实现:

    可以看到,List继承了Collection接口,而Collection接口继承了Iterable接口。其中还有AbstractCollection和AbstractList的实现,用于List对象的公共部分代码的复用:

    public interface List<E> extends Collection<E> {
        // Query Operations
    public interface Collection<E> extends Iterable<E> {
        // Query Operations
    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
        /**
         * Sole constructor.  (For invocation by subclass constructors, typically
         * implicit.)
         */
        protected AbstractList() {
        }
    public abstract class AbstractCollection<E> implements Collection<E> {
        /**
         * Sole constructor.  (For invocation by subclass constructors, typically
         * implicit.)
         */
        protected AbstractCollection() {
        } 

    具体这三个接口定义的方法如下:

    现在来看看List接口的具体特性:

    1、Vector

    Vector类是通过数组实现的,支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它的效率不是很高。

    现在来看一下Vector的成员变量和其中部分的构造方法:

    public class Vector<E>
        extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
        /**
         * The array buffer into which the components of the vector are
         * stored. The capacity of the vector is the length of this array buffer,
         * and is at least large enough to contain all the vector's elements.
         *
         * <p>Any array elements following the last element in the Vector are null.
         *
         * @serial
         */
        protected Object[] elementData;
    
        /**
         * The number of valid components in this {@code Vector} object.
         * Components {@code elementData[0]} through
         * {@code elementData[elementCount-1]} are the actual items.
         *
         * @serial
         */
        protected int elementCount;
    
        /**
         * The amount by which the capacity of the vector is automatically
         * incremented when its size becomes greater than its capacity.  If
         * the capacity increment is less than or equal to zero, the capacity
         * of the vector is doubled each time it needs to grow.
         *
         * @serial
         */
        protected int capacityIncrement;
    
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -2767605614048989439L;
    
        /**
         * Constructs an empty vector with the specified initial capacity and
         * capacity increment.
         *
         * @param   initialCapacity     the initial capacity of the vector
         * @param   capacityIncrement   the amount by which the capacity is
         *                              increased when the vector overflows
         * @throws IllegalArgumentException if the specified initial capacity
         *         is negative
         */
        public Vector(int initialCapacity, int capacityIncrement) {
        super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
        }

    对数据结构有一定了解的人都知道,使用数组作为存储底层,当存储空间不够了的时候,是会重新分配一块更大的内存空间,然后把原空间的数据全部都copy过去,最后释放原空间,这样会导致程序性能的下降,或者是GC的消耗(Vector是默认扩展1倍)。所以我们对处理的数据量要有一定的预估,初始化Vector的时候指定容量大小。由于对Vector所有动作都添加synchronized关键字,同样会导致执行的性能下降。

    下面是对Vector添加元素的时候的代码:

        /**
         * 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++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
            return true;
        }
        /**
         * This implements the unsynchronized semantics of ensureCapacity.
         * Synchronized methods in this class can internally call this
         * method for ensuring capacity without incurring the cost of an
         * extra synchronization.
         *
         * @see #ensureCapacity(int)
         */
        private void ensureCapacityHelper(int minCapacity) {
        int oldCapacity = elementData.length;
        if (minCapacity > oldCapacity) {
            Object[] oldData = elementData;
            int newCapacity = (capacityIncrement > 0) ?
            (oldCapacity + capacityIncrement) : (oldCapacity * 2);
                if (newCapacity < minCapacity) {
            newCapacity = minCapacity;
            }
                elementData = Arrays.copyOf(elementData, newCapacity);
        }
        }

     2、Stack

    Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展,同样是线程安全的 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法。当操作的集合不存在元素的时候,抛出EmptyStackException。

    public
    class Stack<E> extends Vector<E> {
        /**
         * Creates an empty Stack.
         */
        public Stack() {
        }

     3、ArrayList

    同样,ArrayList内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高(ArrayList在内存不够时默认是扩展50% + 1个)。因此,它适合随机查找和遍历,不适合插入和删除。由于没有添加同步,所以是非线程安全的,执行效率也要比Vector高很多

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
        private static final long serialVersionUID = 8683452581122892189L;
    
        /**
         * The array buffer into which the elements of the ArrayList are stored.
         * The capacity of the ArrayList is the length of this array buffer.
         */
        private transient Object[] elementData;
    
        /**
         * The size of the ArrayList (the number of elements it contains).
         *
         * @serial
         */
        private int size;
    
        /**
         * Constructs an empty list with the specified initial capacity.
         *
         * @param   initialCapacity   the initial capacity of the list
         * @exception IllegalArgumentException if the specified initial capacity
         *            is negative
         */
        public ArrayList(int initialCapacity) {
        super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
        this.elementData = new Object[initialCapacity];
        }
        public void ensureCapacity(int minCapacity) {
        modCount++;
        int oldCapacity = elementData.length;
        if (minCapacity > oldCapacity) {
            Object oldData[] = elementData;
            int newCapacity = (oldCapacity * 3)/2 + 1;
                if (newCapacity < minCapacity)
            newCapacity = minCapacity;
                // minCapacity is usually close to size, so this is a win:
                elementData = Arrays.copyOf(elementData, newCapacity);
        }
        }

     4、LinkedList

    LinkedList是用双向链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    {
        private transient Entry<E> header = new Entry<E>(null, null, null);
        private transient int size = 0;
    
        /**
         * Constructs an empty list.
         */
        public LinkedList() {
            header.next = header.previous = header;
        }
        Entry(E element, Entry<E> next, Entry<E> previous) {
            this.element = element;
            this.next = next;
            this.previous = previous;
        }
        }

    总结:根据不同实现类的特性,选择对应的数据结构,能提高程序的运行效率 。需要注意的是,一条线程在进行list列表元素迭代的时候,另外一条线程如果想要在迭代过程中,想要对元素进行操作的时候,比如满足条件添加新元素,会发生ConcurrentModificationException并发修改异常。当然后来版本的CopyOnWrite容器解决了该问题,后续会介绍。 

  • 相关阅读:
    角色扮演游戏引擎的设计原理
    游戏服务器架构
    小谈网络游戏同步
    What is the single most influential book every programmer should read?
    Research Scientists and Engineers
    关于为什么不推荐使用用户定义表类型的说明
    程序员必须遵守的编程原则
    CacheStrategy缓存
    正能量
    MEF 和 MAF
  • 原文地址:https://www.cnblogs.com/ijavanese/p/3770600.html
Copyright © 2011-2022 走看看