Java集合类—List学习笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sun_shaoping/article/details/82461564

Java集合类— List学习笔记

参考资料:中文版(Google) java8 API - List

Java集合框架中List占用举足轻重的地步,想必每一个java开发都有使用过它。

该文主要介绍List集合(实现)和它相关的工具类,并不对其他集合类深入讨论,只与List集合做对比。

List集合特点

1、有序集合,插入的元素是有先后顺序的

2、允许重复的元素

3、listIterator()迭代器,允许元件插入、更换、双向访问,listIterator(int index)从列表中的指定位置开始的列表迭代器。

List集合与Set集合

参考资料:中文版(Google) java8 API - Set

List集合与Set集合作为java集合框架的顶层接口它们都继承了 Collection,所以它们有Collection集合拥有的全部特性,比如它们都是可以迭代的Iterator

List集合:元素有序可重复,先插入的在前面后插入的在后面,当然也可在指定下标位置查询get(int index)、插入add(int index, E element)、替换set(int index, E element)、删除remove(int index)

Set集合:元素无序不可重复,所有方法都是从父接口Collection继承类,没有自己特有的方法,由于不可重复,可以用它去除集合中重复元素。

List集合实现类

List只是一个顶层集合接口,具体的工作还要交给它的实现类来实现。

ArrayList

参考资料:ArrayList

1、基于java数组实现的,内部是一个Object[]组。

2、由于内部是数组,所以根据下标检索get(int index)和替换set(int index, E element)元素很快。

3、添加元素时,数组长度不足以存放新的元素时,需要扩容,将原有数组复制到新的数组中。

//扩容源码,
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);
}

4、插入元素时,需要将当期下标位置之后的元素整体向后移动一个位置。

5、删除元素时,移除指定下标位置元素,将之后的元素整体向前移动一个位置。

6、非线程安全、不支持多线程操作,线程安全CopyOnWriteArrayList

综上所述:

1、ArrayList更适合检索,不适合频繁的添加、插入、删除。

2、创建ArrayList时,尽量指定一个初始大小ArrayList(int initialCapacity)与实际添加元素数量接近(大于等于)。

LinkedList

LinkedList

参考资料:LinkedList

1、双链表实现了ListDeque接口,内部是Node<E>链表。

2、对于检索get(int index)和替换set(int index, E element)操作,需要遍历双向链表,效率较低。

3、添加元素时,支持链表头部(addFirst(E e))和尾部(addFirst(E e)add(E e) )添加,效率很高

4、插入元素时,先找到下标所在元素位置这个过程需要遍历链表,然后打开链表插入元素,效率比头尾添加低。

5、删除元素时,效率由高到低:

removeFirst()/removeLast()/remove()(删除头元素等价于removeFirst()),头尾删除不需要遍历链表

remove(int index)/remove(Object o),遍历链表找到元素后删除。

综上所述:LinkedList更适合添加和插入(相比ArrayList),根据下标检索不如ArrayList

Vector

参考资料:Vector

1、与ArrayList相同内部使用了数组进行存储。

2、 在ArrayList基础上增加了线程安全特性(方法添加synchronized),数组扩容有所不同Vector添加一个扩容成员变量capacityIncrement

//数组扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //如果capacityIncrement>0扩容capacityIncrement大小,否则扩容oldCapacity大小
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

综上所述:Vector就是线程安全的ArrayList

CopyOnWriteArrayList

参考资料:CopyOnWriteArrayList

1、一个线程安全的变体的ArrayList,内部也是用数组实现的。

2、所有可变操作( addset ,等等),需要复制一个数组,在这个数组上进行操作。比如 add(E e)

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //原有的数组
        Object[] elements = getArray();
        int len = elements.length;
        //复制后的新数组
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        //替换原数组
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

3、对于迭代器Iterator<E>ListIterator<E>不进行变更操作会跑出UnsupportedOperationException异常,因为迭代器使用的是它内部数组实现的,并不是数组副本。

综上所述:

1、多线程共享同一List集合。

2、当遍历操作大大超过可变操作。

小节

本小节介绍了List集合一些常用的实现类,它们内部数据存储结构和使用场景。

Collections#synchronizedList

参考资料:Collections

1、返回由指定列表支持的同步(线程安全)List

2、使用委派模式,委派给传入的List,在其基础上添加一个同步锁。如:get(int index)

public E get(int index) {
    synchronized (mutex) {return list.get(index);}
}

锁对象可以自己制定synchronizedList(List<T> list, Object mutex),默认是this(Collections.SynchronizedRandomAccessListCollections.SynchronizedList)对象。

3、有两种实现

Collections.SynchronizedRandomAccessList:实现随机访问接口RandomAccess,如果传入list实现了该接口

Collections.SynchronizedList:不支持随机访问。

4、与Vector的区别

Vector

基于数组实现的线程安全类。

方法同步,锁对象是this

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

迭代器方法iterator()listIterator()有同步锁。

Collections#synchronizedList

内部没有任何具体实现,所有方法加上同步锁后依赖于传入的List集合。

只要是实现List接口就可以使用该方法,具有很好的扩展性,

代码块同步锁,可以自己指定锁对象。

public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
}

迭代器方法iterator()listIterator()没有添加同步锁,所以在使用它们的时候需要手动加锁。

RandomAccess随机访问接口

参考资料:RandomAccess

List实现类使用该接口标记,说明它支持快速随机访问。下面的两个循环,第一个比第二个循环运行得更快,就认为这个List实现类具有快速随机访问,从而可以用RandomAccess标记该类。

  for (int i=0, n=list.size(); i < n; i++)
         list.get(i); 
  for (Iterator i=list.iterator(); i.hasNext(); )
         i.next(); 

List实现类ArrayListLinkedList, 前者使用数组实现它就具有快速随机访问,后者使用链表实现具有顺序访问而不具备快速随机访问,所以在使用fori(第一个)或forEach(第二个)循环时最好使用list instanceof RandomAccess判断一下改类是否支持快速随机访问,如果数据量很大访问速度将得到很明显的提升。

Arrays#asList

1、把数组转换成List集合。

2、使用内部Arrays.ArrayList实现的,它继承了AbstractList,增删改抛出UnsupportedOperationException异常。

3、可以将返回的List转换成ArrayList集合,这样就可以进行增删改了。

总结

本章介绍了与List集合相关的工具类(Arrays#asListCollections#synchronizedList)和一些常用的实现类有线程和非线程安全的,数据结构有所不同(链表和数组),根据具体的使用场景选用不同的实现类。

猜你喜欢

转载自blog.csdn.net/sun_shaoping/article/details/82461564