Java 集合 源码 常见集合源码分析 建材王哥 2025-05-14 2025-05-14
ArrayList 底层结构 1 transient Object[] elementData;
底层是一个数组,默认大小为10,当数组容量不够时,会进行扩容,扩容大小为原来的1.5倍。
添加元素 1 2 3 4 5 public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; }
扩容 1 2 3 4 5 6 7 8 9 10 11 12 13 private void grow (int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1 ); if (newCapacity - minCapacity < 0 ) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0 ) { newCapacity = hugeCapacity(minCapacity); } elementData = Arrays.copyOf(elementData, newCapacity); }
扩容时,会先计算新的容量大小,然后进行扩容,扩容后,会将原来的数组复制到新的数组中。
删除元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public E remove (int index) { if (index >= size) throw new IndexOutOfBoundsException (outOfBoundsMsg(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 ; return oldValue; }
删除元素时,会将后面的元素向前移动,然后将最后一个元素置为null,方便垃圾回收。
优点
底层是数组,查询速度快
插入和删除元素时,需要移动元素,效率较低
缺点
底层是数组,当数组容量不够时,需要扩容,扩容效率较低
删除元素时,需要移动元素,效率较低
LinkedList 底层结构 1 2 3 4 5 6 7 8 9 10 11 12 13 transient int size = 0 ;transient Node<E> first;transient Node<E> last;
底层是一个双向链表,每个节点都包含一个元素和两个指针,分别指向前一个节点和后一个节点。
添加元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public boolean add (E e) { linkLast(e); return true ; } void linkLast (E e) { 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++; }
添加元素时,会创建一个新节点,然后将最后一个节点指向新节点,如果链表为空,将第一个节点指向新节点。
删除元素 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 public E remove (int index) { checkElementIndex(index); return unlink(node(index)); } E unlink (Node<E> x) { final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null ) first = next; else prev.next = next; if (next == null ) last = prev; else next.prev = prev; x.item = null ; x.next = null ; x.prev = null ; size--; modCount++; return element; }
删除元素时,会找到要删除的节点,然后将前一个节点指向下一个节点,将下一个节点指向前一个节点,最后释放节点。
优点
底层是双向链表,插入和删除元素时,不需要移动元素,效率较高
可以在任意位置插入和删除元素
缺点
底层是双向链表,查询速度较慢
占用内存较多
HashMap 底层结构 1 transient Node<K,V>[] table;
底层是一个数组,每个数组元素是一个链表,当链表长度超过8时,会转换为红黑树。
添加元素 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 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 = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this , tab, hash, key, value); else { for (int binCount = 0 ; ; ++binCount) { if ((e = p.next) == null ) { p.next = newNode(hash, key, value, null ); 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; if (!onlyIfAbsent || oldValue == null ) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null ; }
添加元素时,会先计算key的hash值,然后根据hash值找到对应的数组位置,如果数组位置为空,则直接添加元素,如果数组位置不为空,则判断是否为红黑树,如果是红黑树,则调用红黑树的添加方法,如果不是红黑树,则遍历链表,找到对应的元素,如果找到了,则更新元素的值,如果没有找到,则添加元素到链表的末尾,如果链表长度超过8,则将链表转换为红黑树。
删除元素 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 public V remove (Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null , false , true )) == null ? null : e.value; } final Node<K,V> removeNode (int hash, Object key, Object value, boolean matchValue, boolean movable) { Node<K,V>[] tab; Node<K,V> p; int n, index; if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1 ) & hash]) != null ) { Node<K,V> node = null , e; K k; V v; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; else if ((e = p.next) != null ) { if (p instanceof TreeNode) node = ((TreeNode<K,V>)p).getTreeNode(hash, key); else { do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break ; } p = e; } while ((e = e.next) != null ); } } if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { if (node instanceof TreeNode) ((TreeNode<K,V>)node).removeTreeNode(this , tab, movable); else if (node == p) tab[index] = node.next; else p.next = node.next; ++modCount; --size; afterNodeRemoval(node); return node; } } return null ; }
删除元素时,会先计算key的hash值,然后根据hash值找到对应的数组位置,如果数组位置为空,则直接返回null,如果数组位置不为空,则判断是否为红黑树,如果是红黑树,则调用红黑树的删除方法,如果不是红黑树,则遍历链表,找到对应的元素,如果找到了,则删除元素,如果没有找到,则直接返回null。
优点
底层是数组+链表+红黑树,查询速度较快
可以快速定位元素
缺点
底层是数组+链表+红黑树,占用内存较多
在高并发情况下,容易出现死循环问题
HashMap jdk1.7和jdk1.8的区别
jdk1.7中,HashMap的底层是数组+链表,当链表长度超过8时,会转换为红黑树,而jdk1.8中,HashMap的底层是数组+链表+红黑树,当链表长度超过8时,会转换为红黑树。
jdk1.7中,HashMap在扩容时,会重新计算每个元素的hash值,然后重新分配到新的数组位置,而jdk1.8中,HashMap在扩容时,会直接将原数组中的元素移动到新的数组位置,不需要重新计算hash值。
jdk1.7中,HashMap在扩容时,可能会出现死循环问题,而jdk1.8中,HashMap在扩容时,不会出现死循环问题。
jdk1.7中,HashMap的初始容量为16,而jdk1.8中,HashMap的初始容量为16。
jdk1.7中,HashMap的负载因子为0.75,而jdk1.8中,HashMap的负载因子为0.75。
jdk1.7中,HashMap的扩容方式是2的幂次方,而jdk1.8中,HashMap的扩容方式是2的幂次方。
jdk1.7中,HashMap的迭代器是fail-fast的,而jdk1.8中,HashMap的迭代器不是fail-fast的。