hashmap源码探究

hashmap简介

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

hashmap概念

HashMap又叫哈希表、散列表,是一种以键值对方式存储数据的数据结构,它利用不重复无序的键实现了快速查找。

hashmap实现图

数组+链表+红黑树(jdk8) 每个数组存的是一个entry

为什么hashmap能允许k,v为null,而hashtable不允许

hashmap

hashmap的hash方法源码

解释:hashmap里有个hash方法,进行判断,如果key为null会令hash值为0,否则高16与低16异或,,,其他方法会调用hash方法进行操作,而hashtable会调用hashcode方法进行操作,当key为null时,还调用hashcode方法的话会产生空指针异常(下面有详细介绍)

演示:

hashtable

首先hashtable的源码中没有像hashmap中的hash方法

然后看hashtable的put方法

解析:

  • 第一个框:清楚的表示 如果加入一个value为null则丢出空指针异常
  • 第二个框:可以思考出,如果key为null,还调用hashcode方法,肯定也抛出空指针异常

演示

hashmap的get方法详解

首先get方法的作用为用key取value

get方法源码

从get方法看出先去调用了getnode方法返回值赋给e,可以大致猜测getnode的作用是通过key取到entry对象

getnode方法源码

解析:

第一个if:

if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {

table变量:

resize()方法初始化:就是一个数组 也就是开头的那个数组

再看第一个if :判断数组是否为null,数组长度是否大于0,还有一个与运算求出在数组中的也是其链表第一个的entry是否为null,有一个不成立则return null;

简单测试:

第二个if

if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;

其中first

first = tab[(n - 1) & hash]

表的长度减1和key的hash取与 找出entry所在链表的第一个entry

判断hash值,key值key的equals是否相等 若相等返回第一个

第三个if

if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }

第三个if是用了do while循环遍历hash值相等的该链表

key.equals(k):用了equels,这也是为什么对象作为key需要重写hashcode和equals的原因

找到就返回该entry

看完这个方法,再看get方法就容易理解了

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

总结:通过getNode去找,找不到就返回null,找到了就返回其valuse值

hashmap的put方法详解

put源码

 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

参数解释

 /**
     * Implements Map.put and related methods.
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     		只有为真时,才不要更改现有值   
     * @param evict if false, the table is in creation mode.
     	如果为false,则该表处于创建模式。
     * @return previous value, or null if none
     */
首先判断table,也就是数组是否为空,为空的话就去使用inflateTable的方法(这里不多解释)初始化hashmap。

   如果table不为空的话,就判断key是否为空,为空的话就将放到数组的index=0的位置,如果value不为空则返回value值。

   如果key不为空的话,就通过key获取hash值,通过hash值和table的长度与运算获取hashCode值。

   通过hashCode的遍历entry<K,V>的键值对,如果key的hash值相等 并且key.equals(e.key)也相等的话,就将新的value替换掉旧的,返回旧值。(这里借用别人的总结)

https://www.cnblogs.com/houstao/p/8271362.html

我使用的是jdk11 和jdk8的源码有差异,但原理基本不变

如有错误,请留言告诉我

原文地址:https://www.cnblogs.com/sm1128/p/11458671.html