Java 集合框架 (The Collections Framework) 之 List 浅析:底层实现、随机访问与线程安全性

一、List 实现类之 UML 类图

在这里插入图片描述
截取自本人博文: Java 集合框架 (The Collections Framework) 之 UML 类图

附:CopyOnWriteArrayList (List接口的直接子实现类)

二、ArrayList

2.1 ArrayList 的类声明
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//
}

  根据 ArrayList 的类声明可以知道,ArrayList 支持随机访问、支持克隆、支持序列化

2.2 ArrayList 的成员变量

在这里插入图片描述
   可见,ArrayList 的底层实现是Object数组
   在类声明可以知道,ArrayList 支持序列化,但是从成员变量声明可知,ArrayList 的底层存储 elementData 不支持序列化,因为存在 transient 关键字?关于 transient 关键字说明如下:

  • 被transient关键字修饰过得类成员变量能被序列化,但是类需要实现 Exteranlizable 接口、重写 writeExternal(ObjectOutput out)readExternal(ObjectInput in)并自己指定是否序列化哪些属性
    • out.writeObject(elementData);
    • elementData = (Object[]) in.readObject();
  • 静态变量即使没有transient关键字修饰也不会被序列化;

关于 transient 关键字详细内容可见:java中的关键字transient,这篇文章你再也不发愁了

2.3 ArrayList 构造方法
  • 空构造方法
	/**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

   ArrayList 的 空构造方法构建一个初始容量为10的空列表。但是,通过private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};可以发现,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 其实为空数组,那么如何理解呢?首先,我们查看一下ArrayList的Add方法:

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

  假设我们使用空构造方法构造了一个ArrayList,当我们使用add(E e)方法添加第一个元素时,显然会调用ensureCapacityInternal(int minCapacity),如下所示

	private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

  由于size是int型的成员变量,使用空构造方法构造ArrayList时默认为0,那么此时minCapacity为1。然后我们继续查看
calculateCapacity(Object[] elementData, int minCapacity) 的代码:

	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

  由于使用空构造方法构造ArrayList时,elementData已经赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,故方法返回DEFAULT_CAPACITY的值10。
  继而看向ensureExplicitCapacity(int minCapacity)方法:

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

  由于minCapacity为10,而elementData.length此时为0,故而转向private void grow(int minCapacity)方法:

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    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);
    }

  显然,执行到elementData = Arrays.copyOf(elementData, newCapacity)时,newCapacity为10,故而得到的elementData的初始大小为10,然后即执行elementData[size++] = e对第一个元素(index=0,即elementData[0]=e)赋值,size变为1。

  • 带参构造方法public ArrayList(int initialCapacity){}
    /**
     * 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);
        }
    }

  该构造方法构造一个初始容量为initialCapacity的ArrayList,我们考虑一下initialCapacity为0时(elementData 赋值为EMPTY_ELEMENTDATA,确实初始容量为0),第一个元素的插入情况。首先还是执行add(E e)方法,然后是ensureCapacityInternal(int minCapacity)(minCapacity为0),接着是calculateCapacity(Object[] elementData, int minCapacity),由于elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA,故而返回值为1,继而执行ensureExplicitCapacity(int minCapacity)grow(int minCapacity),使得elementData的容量为1,然后执行elementData[size++] = e,完成插入操作,size变为1。

  • 带参构造方法public ArrayList(Collection<? extends E> c){}
    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

  上述代码中,比较难以理解的是:

if (elementData.getClass() != Object[].class)
	elementData = Arrays.copyOf(elementData, size, Object[].class);

  针对于此,我们编写一段代码来说明其作用:

import java.util.*;

/**
 * @author 32098
 */
public class Test {
    public static void main(String[] args) {
        System.out.println("########################");
        Object[] strs = new String[]{"a", "b", "c"};
        System.out.println(strs.getClass());
        // strs[0] = new Object(); 之所以注释掉是因为会出现运行时异常,故采用try的原因使后面代码正常运行
        try {
            strs[0] = new Object();
        }catch (Exception e) {
            // java.lang.ArrayStoreException: java.lang.Object ? 为什么Object[]不能存储Object实例?
            e.printStackTrace();
        }
        if(strs.getClass() != Object[].class){
            strs = Arrays.copyOf(strs, strs.length, Object[].class);
        }
        System.out.println(strs.getClass() + " " + Object[].class);
        // 经过strs = Arrays.copyOf(strs, strs.length, Object[].class),Object[]能存储Object实例
        strs[1] = new Object();
        // 使用构造方法构造ArrayList
        System.out.println("########################");
        List<Object> list = new ArrayList<>(Arrays.asList("a", "b"));
        Object[] elementData = Arrays.asList("a", "b").toArray();
        System.out.println(list.getClass());
        System.out.println(elementData.getClass());
        list.set(0, new Object());
        // java.lang.ArrayStoreException: java.lang.Object
        elementData[0] = new Object();
    }
}

  以下是运行结果:
在这里插入图片描述
  因此,那部分的代码是为了使 elementData 的 class 由class [Ljava.lang.String;改为class [Ljava.lang.Object;,使elementData的class符合于Object[].class,使与声明transient Object[] elementData;一致,从而能够使类似list.set(0, new Object());的语句不出现 ArrayStoreException

2.4 ArrayList 的线程安全性

  通过查看源码,ArrayList 的方法均没有synchronized关键字且方法体无任何锁机制,故而是线程不安全的。
在这里插入图片描述

三、Vector

3.1 Vector 的类声明
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
}

  根据 Vector 的 类声明可知,Vector 支持随机访问、支持克隆、支持序列化

3.2 Vector 的成员变量

在这里插入图片描述

  • 根据成员变量可知,Vector 的底层实现是Object数组:elementData;
  • elementCount:Vector 包含的元素数量;
  • capacityIncrement:向量容量的增加数,如果为0或小于0,向量容量加倍,否则向量容量加+capacityIncrement,见如下代码
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }
3.3 Vector 的构造方法
  • 构造方法集A:
    在这里插入图片描述
      上图三个构造方法比较简单,容易看懂理解

  • 构造方法集B:
    在这里插入图片描述
      仅 if 语句块比较难以理解,但是该内容已经在 ArrayList 的构造方法中已提及,如果认真阅读了上文 ArrayList 的构造方法部分,那么就不存在任何问题。

3.4 Vector 的线程安全性

在这里插入图片描述

  通过查看 Vector 源码,Vector 是线程安全的,因为 Vector 的方法直接或间接的都包含synchronized关键字;

四、Stack

4.1 Stack 的底层实现

在这里插入图片描述

  根据源码,Stack 的底层实现是依赖于 Vector 的方法来实现 LIFO(Last In, First Out) 的。

4.2 Stack 的线程安全性

  通过查看 Stack 源码,Stack 是线程安全的,因为其方法直接或间接的包含synchronized关键字;

五、LinkedList

5.1 LinkedList 类声明
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
//
}

  根据 Vector 的 类声明可知,Vector 不支持随机访问,支持克隆、支持序列化

5.2 LinkedList 成员变量

在这里插入图片描述

  • 根据上图可得:
    • LinkedList 的成员变量包括一个int型的size、两个Node(分别为第一个节点与最后一个节点),不能被序列化
    • LinkedList 是双向链表
  • Node 是什么?
      查看 LinkedList 的源码可知,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 的底层实现是 Node 内部类。

5.3 LinkedList 构造方法
  • 空构造方法
    /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }

  构建一个空链表。

  • 带参构造方法:从集合构造链表
    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param  c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

  addAll()方法

    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }
    public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }

        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

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

        size += numNew;
        modCount++;
        return true;
    }

  根据成员变量注释,不难理解该构造方法构造链表的过程:

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
5.4 LinkedList 的线程安全性

在这里插入图片描述
  通过查看 LinkedList 的源码,LinkedList 是线程不安全的,因为它的所有方法均不存在synchronized关键字且方法体无任何锁机制。

六、CopyOnWriteArrayList

6.1 CopyOnWriteArrayList 的类声明
public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable 
{
//
}

  根据 CopyOnWriteArrayList 的 类声明可知,CopyOnWriteArrayList 支持随机访问、支持克隆、支持序列化。

6.2 CopyOnWriteArrayList 的成员变量
    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();

    /** The array, accessed only via getArray/setArray. */
    private transient volatile Object[] array;

  第一个成员变量lock,类型为 ReentrantLock ,显然是用于保证 CopyOnWriteArrayList 的线程安全性的;第二个成员变量array,即 CopyOnWriteArrayList 的底层存储,因此 CopyOnWriteArrayList 的底层是基于 Object 数组的。

6.3 CopyOnWriteArrayList 的构造方法
6.4 CopyOnWriteArrayList 的线程安全性

  CopyOnWriteArrayList 是线程安全的,原因是:CopyOnWriteArrayList 的写方法体要么使用了独占锁lock,要么使用了写时复制(修改对象时,复制一份,并在复制的对象中修改并在修改后更改引用为新复制的对象,这可能出现的问题是:数据一致性问题,因为在修改后更改引用前,其他线程读到的数据仍然是修改前的数据)

  • 使用独占锁的情况
    /**
     * Replaces the element at the specified position in this list with the
     * specified element.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            E oldValue = get(elements, index);

            if (oldValue != element) {
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len);
                newElements[index] = element;
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }
    /**
     * 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) {
        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();
        }
    }
  • 写时复制的情况
    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If this list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * {@code i} such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns {@code true} if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return {@code true} if this list contained the specified element
     */
    public boolean remove(Object o) {
        Object[] snapshot = getArray();
        int index = indexOf(o, snapshot, 0, snapshot.length);
        return (index < 0) ? false : remove(o, snapshot, index);
    }

    /**
     * A version of remove(Object) using the strong hint that given
     * recent snapshot contains o at the given index.
     */
    private boolean remove(Object o, Object[] snapshot, int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] current = getArray();
            int len = current.length;
            if (snapshot != current) findIndex: {
                int prefix = Math.min(index, len);
                for (int i = 0; i < prefix; i++) {
                    if (current[i] != snapshot[i] && eq(o, current[i])) {
                        index = i;
                        break findIndex;
                    }
                }
                if (index >= len)
                    return false;
                if (current[index] == o)
                    break findIndex;
                index = indexOf(o, current, index, len);
                if (index < 0)
                    return false;
            }
            Object[] newElements = new Object[len - 1];
            System.arraycopy(current, 0, newElements, 0, index);
            System.arraycopy(current, index + 1,
                             newElements, index,
                             len - index - 1);
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

附:关于写时复制,可查看:CopyOnWriteArrayList

七、总结

7.1 List 实现类的线程安全性
  • ArrayList 是线程不安全的
  • Vector 是线程安全的
  • Stack 是线程安全的
  • LinkedList 线程不安全的
  • CopyOnWriteArrayList 是线程安全的
7.2 List 实现类的底层实现
  • ArrayList 的底层实现是 Object 数组
  • Vector 的底层实现是 Object 数组
  • Stack 的底层实现是 Object 数组 (Stack 底层是基于 Vector 的,故其底层实现基于 Object 数组)
  • LinkedList 的底层实现是 Node 内部类
  • CopyOnWriteArrayList 的底层实现是 Object 数组
7.3 List 实现类的随机访问性
  • ArrayList 支持随机访问
  • Vector 支持随机访问
  • Stack 不支持随机访问
  • LinkedList 不支持随机访问
  • CopyOnWriteArrayList 支持随机访问
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值