Java集合框架1HashMap

HashMap定义

  • 1 本文以jdk7为准进行说明
package java.util;
import java.io.*;
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Serializable{
        static final Entry<?,?>[] EMPTY_TABLE = {}; // 空桶
        transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; // 桶容器
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;  // 默认容量
        static final float DEFAULT_LOAD_FACTOR = 0.75f;  // 默认负载因子
       
        transient int modCount;
        // 其他成员变量和方法。。。
}
  • 2 主要成员属性
    1)table属性是一个数组,数组的元素是Entry<?, ?>,Entry保存的是key-value键值对。
    2)DEFAULT_INITIAL_CAPACITY属性是默认容量,大小为16,是HashMap的默认size值。
    3)DEFAULT_LOAD_FACTOR 属性是默认负载因子,当HashMap的size超过容量 X 负载因子时,HashMap经被扩容。
    4)modCount成员属性用来实现“fast-fail”机制(也就是快速失败)。即在并发集合中,其进行迭代操作时,若有其他线程对其结构性的修改,这是迭代器会立马感知到,并且立刻抛出ConcurrentModificationException异常。

HashMap的数据结构

  • 1 当初始化 HashMap 时,系统会创建一个长度为16的Entry数组,数组里存储的元素Entry也被称为“桶(bucket)”,每个 bucket 都有其指定索引,对于HashMap及其子类而言,它们采用Hash算法来计算元素的索引值,即hash(key.hashCode())。
  • 2 如果同时有两个以上的Entry的key.hashCode()一样,那么通过Hash算法计算出来的hash值也一样,这样就发生了hash碰撞,会导致该索引位置同时有多个Entry元素;
  • 3 但是HashMap 的每个“桶”只能存储一个元素,这种情况下,会通过Entry里的next引用,将这几个Entry组成一个链表(Java8中,当该链表长度大于8时,会将链表转变成红黑树,来提高查询速度)。

HashMap源码分析

  • 1 put方法详解
// put方法
public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }

    if (key == null) {
        return putForNullKey(value);
    }          

    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
    return null;
}
// addEntry方法
public void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }
    createEntry(hash, key, value, bucketIndex);
}

1)判断table是否为空,如果为空则扩充table,其中包括确保table的大小为2的整数倍。
2)如果key值为null,则特殊处理,调用putForNullKey(V value),hash值为0,存入table中,返回。
3)如果key值不为null,则计算key的hash值;
4)计算key在table中的索引index;
5)通过索引找到table[index]位置上的Entry“桶”,如果该桶为null,则直接进行增加操作;
6)如果该桶不为null,对其进行遍历,如果发现链表中有Entry的的key一致(hash和equals都一致),则用新值(value)替换旧值(oldValue),并保证key的唯一性;如果Entry“桶”内的所有key没有一个和传进来的key一致的,则进行增加操作。
7)增加操作前先判断是否需要扩容,然后将新加的Entry放到table数组中,之前在table数组中的Entry则被新加的Entry的next引用所指向。例如插入的顺序,原先table[index]的Entry链表是 old1->old2->old3,插入新值之后为new1->old1->old2->old3。

  • 2 get方法详解
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;   
}   

1)判断key值是否为null,如果是则特殊处理(在table[0]的链表中寻找)
2)否则计算hash值,进而获得table中的index值
3)在table[index]的链表中寻找,根据hash值和equals()方法获得相应的value。
4)如果找不到,最后返回null。

使用自定义的类的对象作为键

  • 1 重写自定义类的equals()和hashCode()方法
  • 2 当对象插入到Map中之后不能再被改变

HashMap中的序列化问题

  • 1 HashMap实现了Serializable接口,但是对table的定义(transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;)却是transient(不再是对象序列化的一部分)的。
  • 2 HashMap的put与get基于hashCode的实现,而hashCode是native方法,对每一个不同的java环境来说,同一个key所计算的hashCode是不相同的,所以反序列化后table的index会发生变化,无法还原
  • 3 HashMap默认size到达阈值threshold之后进行扩容,很明显HashMap不可能保证每一个bullet都有数据,很多都为null,如果对这部分数据进行序列化则造成不必要的资源浪费。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,253评论 0 16
  • 实际上,HashSet 和 HashMap 之间有很多相似之处,对于 HashSet 而言,系统采用 Hash 算...
    曹振华阅读 2,508评论 1 37
  • 5.1、对于HashMap需要掌握以下几点 Map的创建:HashMap() 往Map中添加键值对:即put(Ob...
    rochuan阅读 663评论 0 0
  • 距离我申请公众号发送第一篇公众号推送到现在,已经过了131天20小时22分35秒。四个月来,我每天慢悠悠的上班,慢...
    喳西阅读 375评论 0 0
  • 桃花结 一朝春风入长安, 百花争放迷人眼。 喧市城外独吐言 十里桃花伴君眠。
    不今心阅读 285评论 3 4