HashMap深度解析

来源:互联网 发布:java反射得到字段的值 编辑:程序博客网 时间:2024/06/08 00:18

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">hashmap是一种以键值对存储的数据容器,根据key的hashcode值决定存储位置</span>

1、内部存储对象是Entry<Key,Value>;

2、内部的每个对象都会生产一个hashcode值,在存储Entry<Key,Value>时根据Key的hashcode以一定的映射关系存储;

3、获取数据时,根据Key的hashcode和对应的映射关系,直接定位数据的位置。

HashMap内部维护的是数组+链表的结构来存储Entry<Key,Value>。数组的形式是Entry[] table数组,初始化HashMap的时候数组的长度默认为16,Entry[] table 数组的每个元素要么为null要么是由Entry<Key,Value>组成的链表。HashMap中的Entry<Key,Value>的个数为HashMap的大小(size)。

下面介绍几个概念:

阀值(threshold)=容量(capacity)*加载因子(load factor)
容量(capacity):是指HashMap内部Entry[] table线性数组的长度
加载因子(load factor):默认为0.75
阀值(threshold):当HashMap大小超过了阀值,HashMap将扩充2倍,并且rehash。

两个重要的方法:put/get

put()方法-向HashMap存储键值对<Key,Value>

a.  获取这个Key的hashcode值,根据此值确定应该将这一对键值对存放在哪一个桶中,即确定要存放桶的索引;

b.  遍历所在桶中的Entry<Key,Value>链表,查找其中是否已经有了以Key值为Key存储的Entry<Key,Value>对象,

c1. 若已存在,定位到对应的Entry<Key,Value>,其中的Value值更新为新的Value值;返回旧值;

c2. 若不存在,则根据键值对<Key,Value> 创建一个新的Entry<Key,Value>对象,然后添加到这个桶的Entry<Key,Value>链表的头部。

d.  当前的HashMap的大小(即Entry<key,Value>节点的数目)是否超过了阀值,若超过了阀值(threshold),则增大HashMap的容量(即Entry[] table 的大小),并且重新组织内部各个Entry<Key,Value>排列。


/**     * 将<Key,Value>键值对存到HashMap中,如果Key在HashMap中已经存在,那么最终返回被替换掉的Value值。     * Key 和Value允许为空     */    public V put(K key, V value) {            //1.如果key为null,那么将此value放置到table[0],即第一个桶中    if (key == null)            return putForNullKey(value);    //2.重新计算hashcode值,        int hash = hash(key.hashCode());        //3.计算当前hashcode值应当被分配到哪一个桶中,获取桶的索引        int i = indexFor(hash, table.length);        //4.循环遍历该桶中的Entry列表        for (Entry<K,V> e = table[i]; e != null; e = e.next) {            Object k;            //5. 查找Entry<Key,Value>链表中是否已经有了以Key值为Key存储的Entry<Key,Value>对象,            //已经存在,则将Value值覆盖到对应的Entry<Key,Value>对象节点上            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//请读者注意这个判定条件,非常重要!!!                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }        modCount++;        //6不存在,则根据键值对<Key,Value> 创建一个新的Entry<Key,Value>对象,然后添加到这个桶的Entry<Key,Value>链表的头部。        addEntry(hash, key, value, i);        return null;    }    /**     * Key 为null,则将Entry<null,Value>放置到第一桶table[0]中     */    private V putForNullKey(V value) {        for (Entry<K,V> e = table[0]; e != null; e = e.next) {            if (e.key == null) {                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }        modCount++;        addEntry(0, null, value, 0);        return null;    }
get方法:

a.  获取这个Key的hashcode值,根据此hashcode值决定应该从哪一个桶中查找;

b.  遍历所在桶中的Entry<Key,Value>链表,查找其中是否已经有了以Key值为Key存储的Entry<Key,Value>对象;

c1. 若已存在,定位到对应的Entry<Key,Value>,返回value;

c2. 若不存在,返回null

**     * Returns the value to which the specified key is mapped,     * or {@code null} if this map contains no mapping for the key.     *  返回key对应的Value值,如果HashMap中没有,则返回null;     *  支持Key为null情况     * <p>More formally, if this map contains a mapping from a key     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :     * key.equals(k))}, then this method returns {@code v}; otherwise     * it returns {@code null}.  (There can be at most one such mapping.)     *     * <p>A return value of {@code null} does not <i>necessarily</i>     * indicate that the map contains no mapping for the key; it's also     * possible that the map explicitly maps the key to {@code null}.     * The {@link #containsKey containsKey} operation may be used to     * distinguish these two cases.     *     * @see #put(Object, Object)     */    public V get(Object key) {        if (key == null)            return getForNullKey();        int hash = hash(key.hashCode());        //遍历列表        for (Entry<K,V> e = table[indexFor(hash, table.length)];             e != null;             e = e.next) {            Object k;            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))                return e.value;        }        return null;    }
Hashmap中删除节点:

1. 根据Key的hashcode 值和Key定位到Entry<key,Value> 对象在HashMap中的位置;

2. 由于Entry<Key,Value>是一个链表元素,之后便是链表删除节点的操作了;



0 0
原创粉丝点击