• Java集合知识点速查(JDK8)


    集合体系

    «interface»
    Collection
    «interface»
    List
    Vector
    Stack
    ArrayList
    LinkedList
    Set
    HashSet
    LinkedHashSet
    «interface»
    NavigableSet
    TreeSet
    «interface»
    Queue
    PriorityQueue
    «interface»
    SortedSet
    «interface»
    Deque
    ArrayDeque
    «interface»
    Map
    «interface»
    SortedMap
    HashTable
    NavigableMap
    TreeMap
    HashMap
    LinkedHashMap

    Collection

    单列集合层次结构中的根接口,集合表示一组对象,称其为元素。

    • 特点:有些集合允许重复元素,而有些集合则不允许;有些是有序的,有些是无序的;有些集合实现对它们可能包含的元素有限制;有些实现对其元素的类型有限制。试图添加不符合条件的元素会抛出未检查的异常,试图查询不合格元素的存在可能会抛出异常,或者简单地返回false
    • 破坏性方法:Collection接口中包含的破坏性方法是指可以修改其操作所针对的集合的方法,使用破坏性方法操作一个不支持该方法的集合或无效化该方法的集合时会抛出UnsupportedOperationException异常。
    • 线程安全:每个集合决定自己的同步策略。在没有更强的实现保证的情况下,调用正在被另一个线程修改的集合上的任何方法都可能导致未定义的行为。

    List

    List集合存储的元素是有序的,可重复的。

    • ArrayListArrayList支持随机访问、允许所有元素、线程不安全。底层由Object[]动态数组实现。除了实现List接口之外,还提供了一些方法来操作底层的动态数组。每个ArrayList实例都有一个容量。容量是底层动态数组的容量。它至少和动态数组元素的个数即ArrayList的大小(size)一样大。当向ArrayList添加元素时,它的容量会自动增加。应用程序可以使用ensureCapacity()方法在添加大量元素之前增加ArrayList实例的容量,这可能会减少自动扩容的次数。
    • LinkedListLinkedList不支持随机访问、允许所有元素、线程不安全。底层由双向链表实现。需要用到LinkedList的场景几乎都可以使用ArrayList来代替。并且效率往往更高。
    • VectorList接口的古老实现类,是线程安全的,通常不会使用。
    • Stack:已被淘汰。

    ArrayList核心源码分析

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
    
        //动态数组的默认初始容量
        private static final int DEFAULT_CAPACITY = 10;
    	
    	//动态数组容量的最大值
    	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        //初始化ArrayList并指定容量为0的动态数组的实现
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
        //无参构造时动态数组的实现
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
        
        //底层动态数组,如果它的实现是DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,那么在添加第一个元素时会扩展至DEFAULT_CAPACITY
        transient Object[] elementData;
    
        //ArrayList的大小
        private int size;
    
    	/**
    	 * 该字段在ArrayList的父类中
    	 * 代表动态数组被修改(该表大小或打乱顺序)的次数
    	 * 该字段被迭代器使用,如果该字段的值发生变化,那么迭代器将抛出ConcurrentModificationException
    	 */
    	protected transient int modCount = 0;
        
        //指定容量的构造方法
        public ArrayList(int initialCapacity) {
        	//如果指定容量大于零,那么新建一个指定容量大小的动态数组
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            //如果指定容量等于零,那么指定动态数组的实现为EMPTY_ELEMENTDATA
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            //否则,抛出异常
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
            }
        }
        
        //无参构造
        public ArrayList() {
        	//指定动态数组实现为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
        //构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
        public ArrayList(Collection<? extends E> c) {
        	//将参数集合转换为数组a
            Object[] a = c.toArray();
            //判断数组a的大小是否不为零
            if ((size = a.length) != 0) {
            	//参数集合是否是ArrayList
                if (c.getClass() == ArrayList.class) {
                	//如果是直接将数组a作为新ArrayList的动态数组实现
                    elementData = a;
                } else {
                	//否则将数组a的内容复制到一个Object数组作为新ArrayList的动态数组实现
                    elementData = Arrays.copyOf(a, size, Object[].class);
                }
            } else {
                //数组a的长度为零则指定ArrayList的动态数组实现为EMPTY_ELEMENTDATA
                elementData = EMPTY_ELEMENTDATA;
            }
        }
    
    	//确认最小需求容量
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
        	//判断动态数组的实现是否是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            	//如果是则返回默认容量和最小需求容量中的最大值
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            //否则直接返回最小需求容量
            return minCapacity;
        }
    }
    	
    	//首先调用calculateCapacity方法确认最小容量
    	//然后调用ensureExplicitCapacity判断是否要扩容
        private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
    	
    	//判断是否要扩容
        private void ensureExplicitCapacity(int minCapacity) {
        	//动态数组被改变的次数加一
            modCount++;
            //判断最小需求容量是否大于动态数组的长度
            if (minCapacity - elementData.length > 0)
            	//如果是则需要扩容
                grow(minCapacity);
        }
    	
    	//实际扩容方法
        private void grow(int minCapacity) {
            //指定旧容量
            int oldCapacity = elementData.length;
            //新容量为旧容量的1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            //判断新容量是否小于最小需求容量
            if (newCapacity - minCapacity < 0)
            	//如果是则新容量等于最小需求容量
                newCapacity = minCapacity;
            //判断容量是否大于MAX_ARRAY_SIZE
            //若超出了,则调用hugeCapacity()来比较最小需求容量和MAX_ARRAY_SIZE
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            //将动态数组扩容至新容量
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
        
    	//比较最小需求容量和MAX_ARRAY_SIZE
        private static int hugeCapacity(int minCapacity) {
        	//如果最小需求容量小于零,则抛出异常
            if (minCapacity < 0)
                throw new OutOfMemoryError();
            //否则返回最小需求容量和MAX_ARRAY_SIZE中的最大值
            return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123

    ArrayList扩容机制

    在使用add()方法添加第一个元素时,就会检查并在需要时对动态数组进行扩容,根据ArrayList实例化方式不同,扩容机制也有所不同:

    • 无参构造:动态数组的实现为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,在添加第一个元素时动态数组被扩容为10(DEFAULT_CAPACITY)。
    • 其它:动态数组的实现要么是一个指定大小的数组,要么就是EMPTY_ELEMENTDATA,此时会根据最小需求容量和实际容量的大小进行扩容,如果需要扩容,那么新容量是旧容量的1.5倍。

    在使用add()方法添加非第一个元素时,都会根据最小需求容量和实际容量的大小进行扩容。

    public boolean add(E e) {
        ensureCapacityInternal(size + 1); 
        elementData[size++] = e;
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其它添加元素的方法也类似,请自行查阅。

    Set

    Set集合存储的元素是不可重复的。SortedSetSet的基础上添加元素排序的功能,NavigableSetSortedSet的基础上添加了查找元素以及反向遍历的功能。

    • HashSetHashSet存储元素无序、允许所有元素、线程不安全。底层由HashMap实现。add方法的参数作为HashMap的建,HashMap的值用一个Object对象填充。
    • LinkedHashSetLinkedHashSet存储元素有序(插入顺序)、允许所有元素、线程不安全。底层由LinkedHashMap实现的。add方法的参数作为LinkedHashMap的建,LinkedHashMap的值用一个Object对象填充。
    • TreeSetTreeSet存储元素有序(根据compareTo()compare()方法排序)、不允许null元素、线程不安全。底层由TreeMap实现的。add方法的参数作为TreeMap的建,TreeMap的值用一个Object对象填充。

    Queue

    Queue集合是单端队列的抽象,除了基本的Collection操作外,Queue集合还提供了附加的插入、提取和检查操作。这些方法以两种形式存在:

    操作\结果抛出异常返回特殊值
    插入队尾add(E e)offer(E e)
    删除队首remove()poll()
    查询队首元素element()peek()

    Deque集合是双端队列的抽象,Deque集合提供的方法也以两种方式存在:

    操作\结果抛出异常返回特殊值
    插入队首addFirst(E e)offerFirst(E e)
    插入队尾addLast(E e)offLast(E e)
    删除队首removeFirst()pollFirst()
    删除队尾removeLast()pollLast()
    查询队首元素getFirst()peekFirst()
    查询队尾元素getLast()peekLast()

    除此之外,Deque集合还可用于抽象栈,并提供了push()pop()方法。

    • ArrayDequeArrayDeque存储元素有序(插入顺序)、不允许null元素、线程不安全。底层由Object[]动态数组和双指针实现。
    • PriorityQueuePriorityQueue存储元素有序(根据compareTo()compare()方法排序)、不允许null元素、线程不安全。底层由平衡二叉堆实现。

    Map

    Map是双列集合的根接口。SortedMapMap基础上添加元素排序的功能,NavigableMapSortedMap的基础上添加了查找元素和反向遍历的功能。

    • HashMapHashMap存储元素无序、支持所有键和值、线程不安全。底层由链地址法(数组和链表)实现的哈希表实现,当数组长度大于64且链表长度大于8时,就会将链表转换为红黑树,以减少搜索时间。HashMap有两个参数会影响其性能:初始容量和加载因子。容量是哈希表中桶的数量(数组容量),初始容量是哈希表创建时的容量。负载因子是在哈希表容量自动增加之前,允许哈希表达到的满度的程度。当哈希表中的条目数量超过负载因子和当前容量的乘积时,哈希表将被重新哈希(即重新构建内部数据结构),这样哈希表的存储桶数量大约是原来的两倍。一般来说,默认的负载因子(0.75)在时间和空间成本之间提供了一个很好的权衡。较高的值会减少空间开销,但会增加查找成本.在设置HashMap的初始容量时,应该考虑HashMap中预期的条目数量及其负载因子,以尽量减少重新哈希的次数。
    • LinkedHashMapLinkedHashMap存储元素有序(插入顺序)、支持所有键和值、线程不安全。底层由链地址法(数组和链表)实现的哈希表以及一个双向链表实现,这个双向链表用于记录元素插入时的顺序。
    • TreeMapTreeMap存储元素有序(根据compareTo()compare()方法排序)、不允许null键、线程不安全。底层由红黑树实现。
    • HashTableHashMap的线程安全版。

    HashMap核心源码分析

    public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
    
    	//桶的默认容量
    	static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    	
    	//桶的最大容量
    	static final int MAXIMUM_CAPACITY = 1 << 30;
    	
    	//默认负载因子值
    	static final float DEFAULT_LOAD_FACTOR = 0.75f;
    	
    	//桶上的结点大于这个值时会转换为红黑树
    	static final int TREEIFY_THRESHOLD = 8;
    	
    	//桶上的结点小于这个值时会转换为链表
    	static final int UNTREEIFY_THRESHOLD = 6;
    	
    	//链表转换成红黑树需要的最少的桶数
    	static final int MIN_TREEIFY_CAPACITY = 64;
    	
    	//哈希表,在第一次使用时初始化,并根据需要调整大小。在分配时,长度总是2的幂
    	transient Node<K,V>[] table;
    	
    	//哈希表中node的数量
    	transient int size;
    
    	/**
    	 * 代表哈希表被修改(该表大小或打乱顺序)的次数
    	 * 该字段被迭代器使用,如果该字段的值发生变化,那么迭代器将抛出ConcurrentModificationException
    	 */
    	transient int modCount;
    
    	//需要进行扩容的临界值(容量x负载因子)
    	int threshold;
    
    	//负载因子
    	final float loadFactor;
    	
    	//链表结点
        static class Node<K,V> implements Map.Entry<K,V> {
        	//键的hash码
            final int hash;
            //键
            final K key;
            //值
            V value;
            //指向下一个结点
            Node<K,V> next;
            //...
        }
    	
    	//树结点
        static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        	//指向父结点
            TreeNode<K,V> parent;
            //指向左孩子
            TreeNode<K,V> left;
            //指向右孩子
            TreeNode<K,V> right;
            //指向
            TreeNode<K,V> prev;    // needed to unlink next upon deletion
            //结点颜色
            boolean red;
            //...
        }
    
    	//构造一个具有指定初始容量和加载因子的HashMap。
        public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
            this.loadFactor = loadFactor;
            //将tableSizeFor方法将指定的初始值转换为2的倍数
            this.threshold = tableSizeFor(initialCapacity);
        }
    	
    	//无参构造
        public HashMap() {
            this.loadFactor = DEFAULT_LOAD_FACTOR;
        }
    
    	//构造一个具有指定初始容量的HashMap
        public HashMap(int initialCapacity) {
            this(initialCapacity, DEFAULT_LOAD_FACTOR);
        }
    
    	//使用map构造一个HashMap
        public HashMap(Map<? extends K, ? extends V> m) {
            this.loadFactor = DEFAULT_LOAD_FACTOR;
            putMapEntries(m, false);
        }
        
    	//计算键的hash码
        static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    
    	//返回给定目标容量的2次方大小。
    	static final int tableSizeFor(int cap) {
            int n = cap - 1;
            n |= n >>> 1;
            n |= n >>> 2;
            n |= n >>> 4;
            n |= n >>> 8;
            n |= n >>> 16;
            return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    HashMap扩容机制

    HashMap通过resize()方法进行扩容,当哈希表未初始化时,该方法会使用默认值初始化哈希表。

    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        //如果哈希表已经初始化且旧容量大于零
        if (oldCap > 0) {
        	//如果旧容量超过最大容量,那么将不在扩容
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            //否则就将容量扩充为原来的二倍
            }else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY){
            	//如果新容量小于最大容量并且大于默认容量,就设置新阈值
    			newThr = oldThr << 1;
    		}
    	//如果哈希表已经初始化且旧阈值大于零 
        }else if (oldThr > 0){
        	//就将新容量置于阈值
    		newCap = oldThr;
    	//如果哈希表没有初始化,就进行默认初始化
    	}else {
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        //如果新阈值等于零,
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            //如果新容量小于最大容量并且当前阈值小于最大容量,那么新誉值就等于当前阈值,否则就等于Integer的最大值
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);
        }
        //设置阈值
        threshold = newThr;
        //哈希表扩容
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        //如果旧哈希表不为null就将旧哈希表中的元素添加到扩容后的哈希表中
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    该方法的流程图如下:

    请添加图片描述

    HashMap存放元素

    HashMapput()方法底层通过putVal()方法存放元素。

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    
    	Node<K,V>[] tab; Node<K,V> p; int n, i;
    	
    	//如果哈希表未初始化或者容量等于零
    	if ((tab = table) == null || (n = tab.length) == 0){
    		//扩容
    		n = (tab = resize()).length;
    	}
    	
    	//如果要存放元素的桶为空
    	if ((p = tab[i = (n - 1) & hash]) == null){
    		//将元素放入桶中
    		tab[i] = newNode(hash, key, value, null);
    	//如果要存放元素的桶不为空
    	}else {
    	    Node<K,V> e; K k;
    	    //如果桶中第一个结点的哈希值和键值都和要插入元素相同
    	    if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))){
    	    	// 将第一个元素赋值给e,用e来记录
    	    	e = p;
    	    //如果第一个结点是红黑树结点
    	    }else if (p instanceof TreeNode){
    	    	//将元素放入红黑树中
    	    	e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    	    //如果第一个结点是链表结点
    	    }else {
    	    	//遍历链表
    	        for (int binCount = 0; ; ++binCount) {
    	        	//如果链表下一个元素为null
    	            if ((e = p.next) == null) {
    	            	//将元素插入到链表中
    	                p.next = newNode(hash, key, value, null);
    	                //如果链表长度大于等于8
    	                if (binCount >= TREEIFY_THRESHOLD - 1){
    	                	//将链表转换为红黑树
    	                    treeifyBin(tab, hash);
    					}
    	                break;
    	            }
    	            //如果产生哈希冲突,跳出循环
    	            if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))){
    	            	break;
    	            }
    	            p = e;
    	        }
    	    }
    	    //如果存在哈希冲突
    	    if (e != null) {
    	        V oldValue = e.value;
    	        // onlyIfAbsent为false或者旧值为null
    	        if (!onlyIfAbsent || oldValue == null){
    	        	//用新值替换旧值
    	        	e.value = value;
    	        }  
    	        // 访问后回调
    	        afterNodeAccess(e);
    	        // 返回旧值
    	        return oldValue;
    	    }
    	}
    	//哈希表的修改次数加一
    	++modCount;
    	//如果哈希表的大小大于阈值
    	if (++size > threshold){
    		//扩容
    	    resize();
    	}
    	// 插入后回调
    	afterNodeInsertion(evict);
    	return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    该方法的执行流程如下:
    请添加图片描述

    HashMap链表转换为红黑树

    只有当链表长度大于阈值并且数组长度大于或者等于 64 的情况下,才会执行转换红黑树操作。

    final void treeifyBin(Node<K,V>[] tab, int hash) {
    	int n, index; Node<K,V> e;
    	//如果哈希表未初始化或者长度小于64
    	if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY){
    		//扩容
    		resize();
    	//如果要转换为红黑树的链表不是空链表
    	}else if ((e = tab[index = (n - 1) & hash]) != null) {
    	    TreeNode<K,V> hd = null, tl = null;
    	    do {
    	        TreeNode<K,V> p = replacementTreeNode(e, null);
    	        if (tl == null){
    	        	hd = p;
    	        }else {
    	            p.prev = tl;
    	            tl.next = p;
    	        }
    	        tl = p;
    	    } while ((e = e.next) != null);
    	    if ((tab[index] = hd) != null){
    			//hd.treeify(tab);
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    集合视图

    所谓容器视图就是指在现有的容器基础上以某种约束条件进行的封装,这种视图容器在没有说明的情况下不属于任何一个已有的容器实现类。

    子视图

    如果单列集合实现了subxxx的方法比如listsubList()方法,这会产生一个子视图,子视图往往不能添加或删除元素,如果我们修改这个子视图那么集合也会跟着变化。

    映射视图

    Map的子视图,从以下视图删除元素时也会将它们从Map中删除,但是不能添加元素。

    Set<Map.Entry<K,V>> extrySet()
    Set<K> keySet()
    Collection<V> values()
    
    • 1
    • 2
    • 3

    不可修改视图

    不可修改视图对现有集合增加了一个运行时的检查。如果发现试图对集合进行修改, 就抛出一个异常,同时这个集合将保持未修改的状态。

    //Collections中的方法
    static <E> List unmodifiableLIst(List<E> c )
    static <E> Set unmodifiab1eSet(Set<E> c )
    static <K,V> Map unmodifiableMap(Map<K,V> c )
    
    • 1
    • 2
    • 3
    • 4

    同步视图

    同步视图用于保证集合线程的安全性。

    //Collections中的方法
    static <E> List synchronizedList(List<E> c )
    static <E> Set synchronizedSet(Set<E> c )
    static <K,V> Map<K,V> synchronizedMap(Map<K, V> c )
    
    • 1
    • 2
    • 3
    • 4

    类型检查视图

    类型检查视图用来对泛型类型可能出现的问题提供调试支持。

    //Collections中的方法
    static <E> List checkedList(List<E> c , Class<E> elementType)
    static <E> Set checkedSet(Set<E> c, Class<E> elementType)
    static <K,V> Map checkedMap(Map<K,V> c , Class<K> keyType,Class<V> valueType)
    static <E> Queue<E> checkedQueue(Queue<E> queue , Class <E>elementType)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    算法|每日一题|做菜顺序|贪心
    手把手带你体验一场属于Linux的学习之旅
    延期公告 | 第五届“强网”拟态防御国际精英挑战赛
    【详细图文】Windows下安装RustRover和配置Rust环境
    2022年金九银十Java面试手册宝典,横扫春招+校招+社招+秋招
    微服务(学习)
    【SpringBoot】一文了解SpringBoot配置高级
    (推荐阅读)H264, H265硬件编解码基础及码流分析
    【云原生 • Kubernetes】搭建 k8s 集群(Kubeadm 方式)
    Java设计模式之访问者模式
  • 原文地址:https://blog.csdn.net/qq_45295475/article/details/105265432