Java 集合深入学习--ArrayList,LinkedList和Vector

ArrayList

概述:

ArrayList基于数组实现,是个动态数组,容量能够自动增长。

ArrayList实现了List接口,RandomAccess接口(通过下标序号进行快速访问),Cloneable接口和序列化接口,源代码如下:

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

ArrayList是非线程安全的, 多线程下使用可以考虑用Collections.synchronizedList(List list)获取线程安全的对象,或者使用concurrent并发包下的CopyOnWriteArrayList对象。

Collections.synchronizedList(List list, Object mutex)实际上是使用传入的list对象构建一个线程安全的对象:

public static <T> List<T> synchronizedList(List<T> list, Object mutex) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }
SynchronizedRandomAccessList/SynchronizedList中使用synchronized修饰词对各方法进行了同步。

可以选择传入锁对象,如果不传,会创建默认锁。

创建:

ArrayList提供了三种方式的构造器:

ArrayList(int initialCapacity);
ArrayList();
ArrayList(Collection<? extends E> c)
如果使用第二种方式,会创建一个容量为0的数组,并在第一次新增元素时初始化为默认容量10。

修改:

ArrayList提供了set(int index, E element),add(int index, E element),addAll(int index, Collection<? extends E> c),remove(int index),remove(Object o),removeAll(Collection<?> c)等方法(部分参数可以省略)用于修改列表内容。

对列表进行修改时,由于数组容量大小是固定的,所以要检查是否会越界。以add方法为例,源代码如下:

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
ensureCapacityInternal(size + 1)用于确保不会出现数组越界的情况,在需要的情况下对数组进行扩容。

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
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);
    }
grow方法就是对数组进行扩容的实现。可以看到,扩容方式为newCapacity = 1.5oldCapacity,即每次新增原容量的一半。 这种扩容操作需要将旧的数据全部copy至扩容后的数组中,所以代价非常高,因此应该尽量避免数组的扩容,在创建ArrayList时,指定数组的大小。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。

删除时,需要对数组进行大量的操作,以remove为例

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;
    }
其中rangeCheck(index)检查index是否越界,modCount用于记录已从结构上修改 此列表的次数,在迭代期间面临并发修改时,它提供了快速失败行为,而不是非确定性行为。 System.arraycopy(elementData, index+1, elementData, index,numMoved)实现数组的复制,该方法支持对自身的复制。由于非末尾位置的增删操作会有大量的数组拷贝工作,所以对ArrayList的非末尾增删操作效率都较低。

remove(Object o)中会用到fastRemove(int index)方法,其与remove(int index)的区别在于吧不需要对index进行验证,另外还不需要返回旧数据。

private void fastRemove(int index) {
        modCount++;
        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
    }


遍历:

获取数组长度根据下标序号遍历即可。或者使用迭代器。


ArrayList与Ventor

相同点:两者都是由数组实现。

不同点:ArrayList是非线程安全的,Vector是线程安全的;ArrayList扩容默认是原容量1.5倍,Vector扩容默认是原容量2倍。由于Vector的所有方法都由synchronized进行同步,所以效率会低。



LinkedList

概述:

LinkedList是基于列表实现的List,实现了List接口、双端队列Deque接口,Cloneable接口,序列化接口。

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

LinkedList是非线程安全集合。

LinkedList包含三个属性:size(数量),first(头元素),last(尾元素)

transient int size = 0;
transient Node<E> first;
transient Node<E> last;
Node对象为LinkedList的节点元素,元素中包含了指向前后元素的引用(双端)
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是由链表实现的,所以对于列表的增删元素效率较高,但是查询时需要遍历列表,效率不高。此外,由于实现了Deque接口,所以LinkedList拥有双端队列的功能。

遍历:

构建Iterator()对象进行遍历。不要使用index进行遍历。


ArrayList与LinkedList的异同

相同点:都实现了List接口,都允许其中元素为null,列表内元素按照插入顺序排序。都是非线程安全的。

不同点:前者以数组实现,支持快速访问,后者以链表实现,不支持快速访问。所以前者按下标查询速度快,后者较慢,遍历查询时差不多。非结尾增删数据时,后者更加高效。

对列表进行遍历时,ArrayList推荐使用get(index)方法进行遍历,而LinkedList一定要使用Iterator方式进行遍历。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值