2.集合-Collection接口

资料:https://gitee.com/chuanqi1995/java

集合介绍

Collection接口介绍

Map接口介绍

List接口

ArrayList源码分析

ArrayList初始化

		/**
     * Default initial capacity.
     */
		//默认初始容量
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
		//空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
		//默认容量的空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
		//list中的数据
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
		//list的大小
    private int size;

无参构造

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
  	//将空数组赋值到存储数据的数组中
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

有参构造

/**
 * 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) {
  	//如果传的参数大于0
    if (initialCapacity > 0) {
        //那么创建一个与该参数大小相等的数组赋值到存储数据的数组中
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) { //如果传的参数等于0
      	//将空数组赋值到存储数据的数组中
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
      	//否则的话抛出异常
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

第1次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) {
  	//size默认为0
    //ensureCapacityInternal(0 + 1)
    ensureCapacityInternal(size + 1);  // Increments modCount!!
  	//list[0] = e
  	//size++ = 1
    elementData[size++] = e;
    return true;
}
//此时minCapacity参数为:1
private void ensureCapacityInternal(int minCapacity) { 
  	//elementData在无参构造的时候赋值为:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  	//calculateCapacity(DEFAULTCAPACITY_EMPTY_ELEMENTDATA, 1)
  	//ensureExplicitCapacity(10)
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//此时elementData参数为:DEFAULTCAPACITY_EMPTY_ELEMENTDATA;minCapacity参数为:1
private static int calculateCapacity(Object[] elementData, int minCapacity) { 
  	//第一次add的时候入了此判断
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
      	//Math.max(10, 1)
      	//return 10;
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
//此时minCapacity参数为:10
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
  	//10 - 0 = 10 > 0进入判断
    if (minCapacity - elementData.length > 0)
        grow(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
 */
//此时minCapacity参数为:10
private void grow(int minCapacity) {
    // overflow-conscious code
  	//oldCapacity = 0
    int oldCapacity = elementData.length;
  	//这里有个>>概念 叫按位右移运算符
  	//oldCapacity >> 1 = 0 >> 1 = 0
  	//newCapacity = 0 = 0 + 0
  	//计算新的容量 新容量为 老容量的1.5倍 第一次为0
    int newCapacity = oldCapacity + (oldCapacity >> 1);
  	// 0 - 10 = -10 < 0 进入此判断
    if (newCapacity - minCapacity < 0)
      	//newCapacity = 10
        newCapacity = minCapacity;
  	//10 - (20亿 - 8) < 0 所以不会进入此判断
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
  	//存储list数据的数组为:elementData = new Object[]{null,null,null,null,null,null,null,null,null,null};
    elementData = Arrays.copyOf(elementData, newCapacity);
}

第2次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) {
  	//size = 1
  	//1 + 1 = 2;
    ensureCapacityInternal(size + 1);  // Increments modCount!!
  	//elementData[1] = e
  	//size++ = 2
    elementData[size++] = e;
    return true;
}
//minCapacity = 2
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//elementData = {e,null,null,null,null,null,null,null,null,null}
//minCapacity = 2
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
  	//不会进入上面的判断了在这里返回2
    return minCapacity;
}
//minCapacity = 2
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
  	//2 - 10 < 0不会进入此判断了
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

第11次add()

public boolean add(E e) {
  	//size = 10
  	//10 + 1 = 11
    ensureCapacityInternal(size + 1);  // Increments modCount!!
  	//elementData[10] = e;
  	//size = 11;
    elementData[size++] = e;
    return true;
}
//minCapacity = 11
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//elementData = {e,e,e,e,e,e,e,e,e,e}
//minCapacity = 11
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
  	//再此返回11
    return minCapacity;
}
//minCapacity = 11
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
  	//11 - 10 = 1 > 0进入此判断
    if (minCapacity - elementData.length > 0)
      	//minCapacity = 11
        grow(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
 */
//minCapacity = 11
private void grow(int minCapacity) {
    // overflow-conscious code
  	//oldCapacity = 10
    int oldCapacity = elementData.length;
  	//oldCapacity >> 1
  	//1010 -> 0101 = 5
  	//10 + 5 = 15
  	//newCapacity = 15
    int newCapacity = oldCapacity + (oldCapacity >> 1);
  	//15 - 11 = 4 > 0不会进入此判断
    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 = {e,e,e,e,e,e,e,e,e,e,null,null,null,null,null}
    elementData = Arrays.copyOf(elementData, newCapacity);
}

get()

/**
 * Returns the element at the specified position in this list.
 *
 * @param  index index of the element to return
 * @return the element at the specified position in this list
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
  	//校验下标
    rangeCheck(index);

    return elementData(index);
}
/**
 * Checks if the given index is in range.  If not, throws an appropriate
 * runtime exception.  This method does *not* check if the index is
 * negative: It is always used immediately prior to an array access,
 * which throws an ArrayIndexOutOfBoundsException if index is negative.
 */
private void rangeCheck(int index) {
  	//如果下标大于等于size(数组长度,那么抛异常)
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

set()

/**
 * Replaces the element at the specified position in this list with
 * the specified element.
 *
 * @param index index of the element to replace
 * @param element element to be stored at the specified position
 * @return the element previously at the specified position
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E set(int index, E element) {
  	//校验下标索引
    rangeCheck(index);
		//获得该下标下老的数据
    E oldValue = elementData(index);
  	//给该下标赋新的值
    elementData[index] = element;
  	//返回老的数据
    return oldValue;
}

remove()

/**
 * Removes the element at the specified position in this list.
 * Shifts any subsequent elements to the left (subtracts one from their
 * indices).
 *
 * @param index the index of the element to be removed
 * @return the element that was removed from the list
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E remove(int index) {
  	//校验下标索引
    rangeCheck(index);

    modCount++;
  	//获取该下标下老的数据
    E oldValue = elementData(index);
		//elementData = {1,2,3,4,5,6,7,8,9,10};
  	//比如要删除下标5的值 6
  	//10 - 5 - 1 = 4
  	//numMoved = 4
    int numMoved = size - index - 1;
  	//4 > 0进入此判断
    if (numMoved > 0)
      	//源数组 开始下标 目标数组 开始下标 长度
      	//{1,2,3,4,5,6,7,8,9,10}
      	//{1,2,3,4,5,7,8,9,10}
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
  	//{1,2,3,4,5,7,8,9,10,null}
    elementData[--size] = null; // clear to let GC do its work
		//返回没删除之前的数据
    return oldValue;
}

FailFast机制

上面在添加、删除的时候,看到过modCount++;代码,那么这个代码是干啥的呢?他是一种快速失败机制,就是为了并发情况下,对象内部结构发生变化的一种防护机制

modCount++;

Demo

import java.util.Iterator;
import java.util.List;

public class ThreadIterator extends Thread{
    private List list;

    public ThreadIterator(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        for (Iterator iterator = list.iterator();iterator.hasNext();){
            iterator.next();
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
import java.util.List;

public class ThreadAdd extends Thread{
    private List list;

    public ThreadAdd(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("循环添加第:" + (i + 1) + "次");
            list.add(i);
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
import java.util.ArrayList;
import java.util.List;

public class ThreadMain {
    private static List list = new ArrayList();
    public static void main(String[] args) {
        new ThreadAdd(list).start();
        new ThreadIterator(list).start();
    }
}

运行main方法,会抛出异常

Exception in thread "Thread-1" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
	at java.util.ArrayList$Itr.next(ArrayList.java:861)
	at ThreadIterator.run(ThreadIterator.java:14)

源码分析

这里会判断modCount和expectedModCount

expectedModCount在初始化时有赋值

意思就是当并发情况下,如果list出现数据安全问题就会抛出【java.util.ConcurrentModificationException】异常

下面画个图理解一下

LinkedList源码分析

LinkedList是一个双向链表,顺序遍历查询比较快,随机查询比较慢。他有一个重要的静态内部类Node

Node

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;
    }
}

初始化

//链表长度
transient int size = 0;

/**
 * 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;

push(11)第1次

/**
 * Pushes an element onto the stack represented by this list.  In other
 * words, inserts the element at the front of this list.
 *
 * <p>This method is equivalent to {@link #addFirst}.
 *
 * @param e the element to push
 * @since 1.6
 */
public void push(E e) {
  	//向头部添加数据
    addFirst(e);
}
/**
 * Inserts the specified element at the beginning of this list.
 *
 * @param e the element to add
 */
public void addFirst(E e) {
  	//向头部添加数据
    linkFirst(e);
}
/**
 * Links e as first element.
 */
private void linkFirst(E e) {
  	//f = null
    final Node<E> f = first;
  	//[null]11[null]
    final Node<E> newNode = new Node<>(null, e, f);
  	//first = [null]11[null]
    first = newNode;
  	//第一次push()会进入此判断
    if (f == null)
      	//last = [null]11[null]
        last = newNode;
    else
        f.prev = newNode;
  	//size = 1
    size++;
  	//modCount = 1
    modCount++;
}

[null]11[null]

push(22)第2次

/**
 * Pushes an element onto the stack represented by this list.  In other
 * words, inserts the element at the front of this list.
 *
 * <p>This method is equivalent to {@link #addFirst}.
 *
 * @param e the element to push
 * @since 1.6
 */
public void push(E e) {
  	//向头部添加数据
    addFirst(e);
}
/**
 * Inserts the specified element at the beginning of this list.
 *
 * @param e the element to add
 */
public void addFirst(E e) {
  	//向头部添加数据
    linkFirst(e);
}
/**
 * Links e as first element.
 */
private void linkFirst(E e) {
  	//f = [null]11[null]
    final Node<E> f = first;
  	//newNode = [null]22[11]
    final Node<E> newNode = new Node<>(null, e, f);
  	//first = [null]22[11]
    first = newNode;
    if (f == null)
        last = newNode;
    else //第二次push进入此else
      	//f = [22]11[null]
        f.prev = newNode;
  	//size = 2
    size++;
  	//modCount = 2
    modCount++;
}

[null]22[11] -> [22]11[null]

add(11)第1次

/**
 * Appends the specified element to the end of this list.
 *
 * <p>This method is equivalent to {@link #addLast}.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
public boolean add(E e) {
  	//向尾部添加数据
    linkLast(e);
    return true;
}
/**
 * Links e as last element.
 */
void linkLast(E e) {
  	//l = null
    final Node<E> l = last;
  	//[null]11[null]
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

[null]11[null]

add(22)第2次

/**
 * Appends the specified element to the end of this list.
 *
 * <p>This method is equivalent to {@link #addLast}.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
public boolean add(E e) {
  	//向尾部添加数据
    linkLast(e);
    return true;
}
/**
 * Links e as last element.
 */
void linkLast(E e) {
  	//l = [null]11[null]
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

[null]11[22] -> [11]22[null]

get()

/**
 * Returns the element at the specified position in this list.
 *
 * @param index index of the element to return
 * @return the element at the specified position in this list
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
  	//校验下标
    checkElementIndex(index);
    return node(index).item;
}
/**
 * Returns the (non-null) Node at the specified element index.
 */
Node<E> node(int index) {
    // assert isElementIndex(index);
		//如果下标小于链表大小的一半,那么从头部开始遍历查找
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else { //如果下标大于等于链表大小的一半,那么从尾部开始遍历查找
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

set()

/**
 * Replaces the element at the specified position in this list with the
 * specified element.
 *
 * @param index index of the element to replace
 * @param element element to be stored at the specified position
 * @return the element previously at the specified position
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E set(int index, E element) {
  	//检测下标
    checkElementIndex(index);
  	//首先找到传入下标参数的原先值
    Node<E> x = node(index);
  	//获取原先值的元素
    E oldVal = x.item;
  	//将原先值赋值为传入的值
    x.item = element;
  	//返回原先值的元素
    return oldVal;
}

Vector源码分析

add()

/**
 * 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;
}

我们可以看到,方法修饰上有个synchronized方法,所以vector是线程安全的。它既然是线程安全的,必然性能就比较低

list不是线程安全的,如果要求必须是线程安全的,怎么处理?

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ThreadMain {
    private static List list = new ArrayList();
    public static void main(String[] args) {
        List list = Collections.synchronizedList(ThreadMain.list);
    }
}

可以用Collections工具类做一个灵活的转换,转换成线程安全的list

/**
 * Returns a synchronized (thread-safe) list backed by the specified
 * list.  In order to guarantee serial access, it is critical that
 * <strong>all</strong> access to the backing list is accomplished
 * through the returned list.<p>
 *
 * It is imperative that the user manually synchronize on the returned
 * list when iterating over it:
 * <pre>
 *  List list = Collections.synchronizedList(new ArrayList());
 *      ...
 *  synchronized (list) {
 *      Iterator i = list.iterator(); // Must be in synchronized block
 *      while (i.hasNext())
 *          foo(i.next());
 *  }
 * </pre>
 * Failure to follow this advice may result in non-deterministic behavior.
 *
 * <p>The returned list will be serializable if the specified list is
 * serializable.
 *
 * @param  <T> the class of the objects in the list
 * @param  list the list to be "wrapped" in a synchronized list.
 * @return a synchronized view of the specified list.
 */
public static <T> List<T> synchronizedList(List<T> list) {
    return (list instanceof RandomAccess ?
            new SynchronizedRandomAccessList<>(list) :
            new SynchronizedList<>(list));
}
public E get(int index) {
    synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
    synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
    synchronized (mutex) {return list.remove(index);}
}

我们可以看到相当于把list的get、set、add、remove方法重写了一遍,加了锁

Set接口

HashSet

源码分析

无参构造

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * default initial capacity (16) and load factor (0.75).
 */
public HashSet() {
    map = new HashMap<>();
}

add()

/**
 * Adds the specified element to this set if it is not already present.
 * More formally, adds the specified element <tt>e</tt> to this set if
 * this set contains no element <tt>e2</tt> such that
 * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
 * If this set already contains the element, the call leaves the set
 * unchanged and returns <tt>false</tt>.
 *
 * @param e element to be added to this set
 * @return <tt>true</tt> if this set did not already contain the specified
 * element
 */
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

可以看出来HashSet本质上是一个HashMap,所以我们下面分析HashMap其实就知道HashSet的原理了

TreeSet

源码分析

无参构造

/**
 * Constructs a new, empty tree set, sorted according to the
 * natural ordering of its elements.  All elements inserted into
 * the set must implement the {@link Comparable} interface.
 * Furthermore, all such elements must be <i>mutually
 * comparable</i>: {@code e1.compareTo(e2)} must not throw a
 * {@code ClassCastException} for any elements {@code e1} and
 * {@code e2} in the set.  If the user attempts to add an element
 * to the set that violates this constraint (for example, the user
 * attempts to add a string element to a set whose elements are
 * integers), the {@code add} call will throw a
 * {@code ClassCastException}.
 */
public TreeSet() {
    this(new TreeMap<E,Object>());
}
/**
 * Adds the specified element to this set if it is not already present.
 * More formally, adds the specified element {@code e} to this set if
 * the set contains no element {@code e2} such that
 * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
 * If this set already contains the element, the call leaves the set
 * unchanged and returns {@code false}.
 *
 * @param e element to be added to this set
 * @return {@code true} if this set did not already contain the specified
 *         element
 * @throws ClassCastException if the specified object cannot be compared
 *         with the elements currently in this set
 * @throws NullPointerException if the specified element is null
 *         and this set uses natural ordering, or its comparator
 *         does not permit null elements
 */
public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

可以看出来TreeSet本质上是一个TreeMap,所以我们下面分析TreeMap其实就知道TreeSet的原理了

posted @ 2022-03-02 18:43  Java-Legend  阅读(39)  评论(0编辑  收藏  举报