HashMap源码分析——put和get(一)

HashMap源码分析——put和get(一)

前言

首先,这是自己的开的不知道多少个坑的笔记了(汗颜),这些笔记都是自己在Java这条道路上的初学阶段所思所想,并没有多少含金量~

想第一篇记一记HashMap,没有为什么,任性。

首先,HashMap的基本方法应该都知道了,put(key,value)以及get(key)
另外值得注意的一个基础问题是HashMap的key、value都不能是基本数据类型(即不能为byte short int long char float double boolean),但是很特别的一点是key和value可以为null

1. 慢着 开始之前复习下异或和右移运算符

1.1 异或的基本运算

异或是二元按位运算符的一种,最基本的概念是在当前位下,两个二进制相同则为0,不同则为1

public static void main(String []args){
    int i = -5;
    int j = 5;
    System.out.println("i : " + Integer.toBinaryString(i));
    System.out.println("j : " + Integer.toBinaryString(j));
    System.out.println("i ^ j : " + Integer.toBinaryString(i ^ j));
    System.out.println("i ^ j : " + (i ^ j));
    /**
    *  output :
    *  i : 11111111111111111111111111111011
    *  j : 101
    *  i ^ j : 11111111111111111111111111111110
    *  i ^ j : -2
    */
}

1.2 异或规律

  • 0异或A结果都为A : 0 ^ 1 = 1 0 ^ 0 = 0
  • 1异或A结果都为A的相反数 : 1 ^ 1 = 0 1 ^ 0 = 0
  • A异或A结果都为0

1.3 异或进阶

关于异或进阶不再赘述,大家可以观看维基百科中的异或讲解

值得一提的是面试经常会考到:如何不使用第三个参数来交换a和b的值,这时候就可以使用异或。

int a = 10;
int b = 41;
a = b ^ a;
b = b ^ a; // b = b ^ ( b ^ a) = a
a = a ^ b; // a = ( b ^ a ) ^ a = b

1.4 左移和右移运算符和它们的规律

左移和右移运算符是奇怪的运算操作符,我刚开始学的时候觉得不就是移位嘛,移位就是了,后来看《Java编程思想》的时候才知道原来左移和右移原来有这样那样的限制……

先从最简单的开始说,左移就是低位补0,右移就是高位补符号位,无符号右移运算符就是高位补0(这里不再赘述)

接着 在一般情况下 左移和右移都有下面的规律 :

  • a << b = a * (2 ^ b)
  • a >> b = a / (2 ^ b)

例如 :

public static void main(String []args){
    int i = 5;
    int j = 32;
    int m = 40;
    System.out.println(3 << i);
    System.out.println(3 << j);
    System.out.println(3 << m);
    /**
    * output :
    * 96
    * 3
    * 768
    */
}

大家可能发现了,有些结果好像与公式并不符合,这是因为int类型的“限制”

众所周知,在Java中int占四个字节,也就是32位,在做移位操作时,无论是左移还是右移,右操作运算符都不能大于或者等于32(当然,我说的是左边的数值是int类型时),因为一旦超过了32,左边的数值完全变了个样。就拿1 << 32而言:

1 << 32

我们发现1 << 32如果按照正常计算的话就会变成0,更别提3 << 32,5 << 91了,如果按照正常流程来的话,只要右操作数大于32,结果都变成了0,当然,Java不可能这样子计算,它采取了一种聪明的操作方式,至于为什么,目前不在讨论范围只内。另一种状况是,如果恰巧将1移到了符号位上,即便移位之前是正的数字,也会变成负的,就像1 << 31 = - 2147483648 那样。所以,在上边说的公式有诸多的限制。

这种聪明的方式就是,在左操作数类型为int时,当右侧数值大于等于32,都可以对右操作数取余操作( % 32),然后再做移位运算,例如:

3 << 40 = 3 << (40 % 32) = 3 << 8 = 3 * (2 ^ 8) = 768

以上都说的是int类型下的移位操作,如果左边操作数为char、byte、short类型都会被转换成int类型处理,当左操作数是long的时候,右边操作数的最大限制变成了64。

2. HashMap

2.1 从HashMap结构说起

HashMap结构如下图所示:

HashMap结构

总的来说,HashMap的结构是:数组+链表+红黑树 (PS : 我用的是8版本的)

HashMap中链表的节点
节点组成的数组
红黑二叉树

目前,现将红黑二叉树看成是一个不一般的二叉树吧(我能说现阶段不知道什么是红黑二叉树嘛XD)。以上三张图分别就是组成HashMap的三种数据结构了。

然后,再来看一下几个HashMap中几个重要的字段 :

/**
* 默认的数组容量(也就是数组的长度) —— 数字必须是二次幂
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* 数组的最大容量
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 当使用无参构造函数时 默认的加载因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
 * 当某个数组下的链表长度超过8时 链表将会转换为红黑树
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* 当节点的数量超过threshold时会发生扩容 此外 当尚未初始化数组的时候,这个字段也会存放初始数组的容量,如果是0的话表示容量是默认的数组容量(16)
*/
int threshold;

什么是加载因子?我搜索了百度,然后一脸懵逼,它是这样介绍的:

加载因子是表示Hsah表中元素的填满的程度。

一脸懵逼,两脸茫然,然后,在进行相关搜索,发现这个比较靠谱

threshold(阈值) = (int)加载因子 * 数组容量

还是有点懵逼!!!意思就是说当 节点数量 > 数组容量 * 加载因子时,数组会扩容.

我们来验证一下吧!!

2.2 HashMap的构造函数

我们通过idea发现HashMap有四种构造函数,先看前三种(毕竟最后一种不是重点):

    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                    initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                    loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }
  • 无参构造函数 : 设置了加载因子为默认加载因子(0.75f)

    PS 再来回顾下加载因子是干嘛的:加载因子是扩容用的 什么时候该扩容呢?就是节点个数大于加载因子✖️数组长度的时候就该扩容了

  • 一个参数的构造函数 : 设置了容量初始容量,然后在源码里面直接this交给了第三个构造参数处理

  • 两个参数的构造函数 : 设置了初始容量以及加载因子,我发现在这个构造函数中好像并没有关于数组初始容量的更多限制啊,刚才源码中不是说初始容量必须是二次幂吗?我传一个13,19,在这个构造参数中也没抛出异常啊!!不急,我们看最后一句 :

this.threshold = tableSizeFor(initialCapacity);

我们传入的初始容量13,18,41……这些“不规范的”(也就是不是二次幂的)数字传给了tableSizeFor这个函数,最终返回给了threshold这个变量中(别忘了,这个变量除了可以记录“扩张阈值”,在数组还没有初始化的时候还可以存放数组的自定义容量,这些都是注释中所标注的东西)。来领略一下tableSizeFor的真正魅力,这个函数可以 :

把任何一个int类型的、大于0的数字转成一个与之相近并大于这个数字的二次幂整数

听上去有点绕口,简单点说15进去了成了16,17进去了出来摇身一变成了32(比17大的最小二次幂整数就是32了),非常神奇的一个函数,第二小篇接着看这个函数。

下一小节 : HashMap源码分析——put和get(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,293评论 18 399
  • HashMap 源码分析 前几篇分析了 ArrayList , LinkedList ,Vector ,Stack...
    醒着的码者阅读 2,807评论 4 44
  • 前言 HashMap HashMap类继承图 HashMap属性 HashMap构造函数HashMap(int i...
    HikariCP阅读 1,873评论 0 5
  • 创建文件 os.mknod(“test.txt”) 创建空文件 open(“test.txt”,w) 直接打开一个...
    咸鱼而已阅读 302评论 0 0
  • 三亚是海南的一个小城,夏天阳光明媚,全部都是海,那里景色宜人,物产丰富,是个美丽的地方。 三亚一带的海面时而平静,...
    叫我公主宝宝阅读 262评论 0 0