HashMap源码分析(基于jdk1.8)

一.概述

  • 源码2397行(java8)
  • 继承了AbstractMap<K,V>,实现了Map<K,V>, Cloneable, Serializable接口
  • 在jdk1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值一次查找的效率较低。而在jdk1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

二.属性

  • private static final long serialVersionUID = 362498820763181265L;
  • static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始化大小为16
  • static final int MAXIMUM_CAPACITY = 1 << 30; //集合大小的最大值,非常大…
  • static final float DEFAULT_LOAD_FACTOR = 0.75f;
  • static final int TREEIFY_THRESHOLD = 8; //相同hash值的元素大于8,则链表转化成红黑树
  • static final int UNTREEIFY_THRESHOLD = 6; //相同hash值的元素小于6,则红黑树转化成链表
  • static final int MIN_TREEIFY_CAPACITY = 64;
  • transient Node<K,V>[] table; //transient表示不用被序列化,一般是临时数据
  • transient Set<Map.Entry<K,V>> entrySet;
  • transient int size;
  • transient int modCount;
  • int threshold;
  • final float loadFactor;

三.方法

  • hash(Object key)
  • comparableClassFor(Object x)
  • compareComparables(Class<?> kc, Object k, Object x)
  • tableSizeFor(int cap)
  • HashMap(int initialCapacity, float loadFactor)
  • HashMap()
  • HashMap(int initialCapacity)
  • HashMap(Map<? extends K, ? extends V> m)
  • putMapEntries(Map<? extends K, ? extends V> m, boolean evict)
  • size()
  • isEmpty()
  • get(Object key)
  • getNode(int hash, Object key)
  • containsKey(Object key)
  • put(K key, V value)
  • putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)
  • resize()
  • treeifyBin(Node<K,V>[] tab, int hash)
  • putAll(Map<? extends K, ? extends V> m)
  • remove(Object key)
  • removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable)
  • clear()
  • containsValue(Object value)
  • keySet()
  • values()
  • entrySet()
  • getOrDefault(Object key, V defaultValue)
  • putIfAbsent(K key, V value)
  • remove(Object key, Object value)
  • replace(K key, V oldValue, V newValue)
  • replace(K key, V value)
  • computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
  • computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
  • compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
  • merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
  • forEach(BiConsumer<? super K, ? super V> action)
  • replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
  • clone()
  • loadFactor()
  • capacity()
  • writeObject(java.io.ObjectOutputStream s)
  • readObject(java.io.ObjectInputStream s)
  • newNode(int hash, K key, V value, Node<K,V> next)
  • replacementNode(Node<K,V> p, Node<K,V> next)
  • newTreeNode(int hash, K key, V value, Node<K,V> next)
  • replacementTreeNode(Node<K,V> p, Node<K,V> next)
  • reinitialize()
  • afterNodeAccess(Node<K,V> p)
  • afterNodeInsertion(boolean evict)
  • afterNodeRemoval(Node<K,V> p)
  • internalWriteEntries(java.io.ObjectOutputStream s)
内部类
  • final class KeySet extends AbstractSet
  • final class Values extends AbstractCollection
  • final class EntrySet extends AbstractSet<Map.Entry<K,V>>
  • abstract class HashIterator
  • final class KeyIterator extends HashIterator implements Iterator
  • final class ValueIterator extends HashIterator implements Iterator
  • final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>>
  • static class HashMapSpliterator<K,V>
  • static final class KeySpliterator<K,V> extends HashMapSpliterator<K,V> implements Spliterator
  • static final class ValueSpliterator<K,V> extends HashMapSpliterator<K,V> implements Spliterator
  • static final class EntrySpliterator<K,V> extends HashMapSpliterator<K,V> implements Spliterator<Map.Entry<K,V>>
  • static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V>

四.HashMap工作原理

当我们把键值对传递给put方法时,他调用键对象的hashCode()方法来计算哈希值,然后找到bucket位置来存储对象。当hashcode一样时,即出现hash冲突时,HashMap使用linikedList来解决碰撞问题,对象会存储在linkedList的下一个节点中。在获取对象时,如果同一hashcode的怎么知道要取哪个键值对呢?这时候,就要通过键对象的equals()方法来判断了。

五.HashMap的数据结构

HashMap底层主要是基于数组和单链表来实现的。哈希数组的每一个元素存储的都是一个单链表的头节点。它之所以有相当快的查询速度,主要是因为它通过计算散列码来决定存储的位置。如果散列码相同,即出现hash冲突。解决hash冲突的方法有很多,HashMap底层是通过链表来解决hash冲突的。

六.HashMap的存储实现

  • HashMap如何保证key值的唯一的,通过equals()方法来判断吗?不是的,如果这样,时间复杂度是O(n)。其实是先用hash方法(HashTable使用hashcode方法)来判断,如果hash值一样,再用equals方法判断,因为如果hash值(hashcode值)不同,那么一定不是同一个key,如果相同,那么不一定是同一个key,需要再比较。所以说,HashMap很少用到equals()。
  • HashMap在存储过程中,并没有将key,value分开存储,而是当成一个整体key-value来处理的,这个整体就是Entry对象。value只是key的一个附属而已。

七.tips

  • HashMap是线程不安全的实现,而HashTable是线程安全的实现,但HashTable效率较低。如果需要线程安全,可以使用ConcurrentHashMap,为什么这个效率就会高些呢,因为它采用来分段锁但技术,维护了一个segment数组,调用put时,先通过key获取到对应的segment,获取某个segment的锁时,不会影响其他分段内的put调用,理论上能同时支持和segment大小相同的线程并发调用。但jdk1.8抛弃了segment分段锁,而是采用了CAS+synchronized来保证并发安全性。
  • 什么时候进行rehash?当HashMap的容量达到threshold(这个值有HashMap的容量*设置的一个影响因子得到)时就需要进行扩容,这个时候就要进行ReHash操作了,会先调用risize进行扩容。在扩容的时候需要进行rehash操作,非常耗时。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值