Java List接口中LinkedList、Vector和ArrayList的区别

List接口用于存放多个元素,能够维护元素的次序,并且允许元素的重复,一共有三个实现类:LinkedList、ArrayList、Vector和LinkedList。其中LinkedList使用的是双向链表结构,增删元素快,查找慢;ArrayList是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长;Vector是一个矢量队列,它是JDK1.0版本添加的类。

ArrayList与Vector相对LinkedList来说都是查找元素快,增删元素慢,前两者都是利用数组实现的,本质上都是动态数组,相似性较大。所以对于List这三个实现类的区别,将从LinkedList和ArrayList性能差异分析与Vector与ArrayList的区别来探讨。

一、LinkedList和ArrayList区别分析

在实现List类时,LinkedList与ArrayList有种互补的既视感,前者增删元素快,后者查找元素快,这里从源码来思考这两个类的性能区别。

1.增删元素区别

LinkedList.java中向指定位置插入元素的代码如下

public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
void linkLast(E e) {
        final Node l = last;
        final Node newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
void linkBefore(E e, Node succ) {
        // assert succ != null;
        final Node pred = succ.prev;
        final Node newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

通过add(int index, E element)向LinkedList插入元素时。先是在双向链表中找到要插入节点的位置index;找到之后,再插入一个新节点,插入节点后无需进行其余额外操作。同时使用add插入节点时,有一个加速动作:若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。

ArrayList.java中向指定位置插入元素的代码如下

public void add(int index, E element) {
        rangeCheckForAdd(index);
        modCount++;
        final int s;
        Object[] elementData;
        if ((s = size) == (elementData = this.elementData).length)
            elementData = grow();
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        elementData[index] = element;
        size = s + 1;
    }
public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

与数组动态扩容类似,增删元素时,先确认ArrayList的容量再进行删减,若容量不够,则增加容量。
注意调用的一个方法: 这个方法是移动index之后所有元素。这就意味着,ArrayList的add(int index, E element)函数,会引起index之后所有元素的改变,这也是LinkedList中插入元素很快,而ArrayList中插入元素很慢的原因了。

2.查找元素性能区别

相对于ArrayList,LinkedList中查找元素(随机访问)的速度很慢

LinkedList随机访问的代码:

// 返回LinkedList指定位置的元素
public E get(int index) {
    return entry(index).element;
}

// 获取双向链表中指定位置的节点
private Entry entry(int index) {
    if (index < 0 || index >= size)
        throw new IndexOutOfBoundsException("Index: "+index+
                                            ", Size: "+size);
    Entry e = header;
    // 获取index处的节点。
    // 若index < 双向链表长度的1/2,则从前先后查找;
    // 否则,从后向前查找。
    if (index < (size >> 1)) {
        for (int i = 0; i <= index; i++)
            e = e.next;
    } else {
        for (int i = size; i > index; i--)
            e = e.previous;
    }
    return e;
}

ArrayList随机访问的代码 :

// 获取index位置的元素值
public E get(int index) {
    RangeCheck(index);

    return (E) elementData[index];
}

private void RangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(
        "Index: "+index+", Size: "+size);
}

再ArrayList随机查找数据元素的时候,当查找数据元素索引小于数组长度的时候,只需要直接return数据元素就好,反观LinkedList,即使利用二分法排除掉一半的数据,还需要利用for循环通过现有节点来访问下一个或者上一个节点的数据来进行查找。

二、Vector和ArrayList比较

Vector和ArrayList相似之处有他们都继承于AbstractList;都实现了RandomAccess接口与Cloneable接口;它们都是通过数组实现的,本质上都是动态数组;它们都支持Iterator和listIterator遍历

主要不同之处有以下几个方面:

1.线程安全性不同

vector是线程(Thread)同步(Synchronized)的,所以它也是线程安全的,而Arraylist是线程异步(ASynchronized)的,是不安全的。ArrayList适用于单线程,Vector适用于多线程。但是由于vector是线程同步的,导致它比ArrayList效率要地上很多

由于只有一条线程能进入到已同步的逻辑当中,在使用同步时,因为需要从主存当中重新获取主存中对象最新的信息,然后对其修改,提交回主存当中,而且申请锁是要会占用系统资源,导致同步操作效率低的情况发生。

2 对序列化支持不同

ArrayList支持序列化,而Vector不支持;即ArrayList有实现java.io.Serializable接口,而Vector没有实现该接口。

3 构造函数个数不同

ArrayList有3个构造函数,而Vector有4个构造函数。Vector除了包括和ArrayList类似的3个构造函数之外,另外的一个构造函数可以指定容量增加系数。

ArrayList构造函数如下:

// 默认构造函数
ArrayList()

// capacity是ArrayList的默认容量大小。当由于增加数据导致容量不足时,容量会添加上一次容量大小的一半。
ArrayList(int capacity)

// 创建一个包含collection的ArrayList
ArrayList(Collection collection)

Vector构造函数如下:

// 默认构造函数
Vector()

// capacity是Vector的默认容量大小。当由于增加数据导致容量增加时,每次容量会增加一倍。
Vector(int capacity)

// 创建一个包含collection的Vector
Vector(Collection collection)

// capacity是Vector的默认容量大小,capacityIncrement是每次Vector容量增加时的增量值。
Vector(int capacity, int capacityIncrement)
4 容量增加方式不同

如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。

ArrayList增长函数:

public void ensureCapacity(int minCapacity) {
        if (minCapacity > elementData.length
            && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
                 && minCapacity <= DEFAULT_CAPACITY)) {
            modCount++;
            grow(minCapacity);
        }
    }

Vector增长函数:

public synchronized void ensureCapacity(int minCapacity) {
        if (minCapacity > 0) {
            modCount++;
            if (minCapacity > elementData.length)
                grow(minCapacity);
        }
    }

总结:

1.对于需要快速插入,删除元素,应该使用LinkedList。
2.对于需要快速随机访问元素,应该使用ArrayList。
3.对于“单线程环境” 或者 “多线程环境,但List仅仅只会被单个线程操作”,此时应该使用非同步的类(如ArrayList)。
4.对于“多线程环境,且List可能同时被多个线程操作”,此时,应该使用同步的类(如Vector)。
5.由于效率的影响,选择ArrayList和Vector时,能尽量选择ArrayList就选择ArrayList,可以自己给ArrayList加锁来实现线程安全。

你可能感兴趣的:(Java List接口中LinkedList、Vector和ArrayList的区别)