Jdk1.7下的HashMap源码分析

常量属性

/**

* The default initial capacity - MUST be a power of two.

* 默认初始容量大小

*/staticfinalintDEFAULT_INITIAL_CAPACITY =1<<4;// aka 16/**

* MUST be a power of two <= 1<<30.

* hashMap最大容量,可装元素个数

*/staticfinalintMAXIMUM_CAPACITY =1<<30;/**

* The load factor used when none specified in constructor.

* 加载因子,如容量为16,默认阈值即为16*0.75=12,元素个数超过(包含)12且,扩容

*/staticfinalfloatDEFAULT_LOAD_FACTOR =0.75f;/**

* 空数组,默认数组为空,初始化后才才有内存地址,第一次put元素时判断,延迟初始化

*/staticfinalEntry[] EMPTY_TABLE = {};

2、存在的死循环问题

扩容导致的死循环,jdk1.7中在多线程高并发环境容易出死循环,导致cpu使用率过高问题,问题出在扩容方法resize()中,更具体内部的transfer方法:将旧数组元素转移到新数组过程中,源码如下:

voidresize(intnewCapacity){    Entry[] oldTable = table;intoldCapacity = oldTable.length;//1.如果原来数组容量等于最大值了,2^30,设置扩容阈值为Integer最大值,不需要再扩容if(oldCapacity == MAXIMUM_CAPACITY) {        threshold = Integer.MAX_VALUE;return;    }//2.创建新数组对象 Entry[] newTable =newEntry[newCapacity];//3.将旧数组元素转移到新数组中,分析一transfer(newTable, initHashSeedAsNeeded(newCapacity));//4.重新引用新数组对象和计算新的阈值table = newTable;    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY +1);}

transfer方法

/**

* Transfers all entries from current table to newTable.

* 从当前数组中转移所有的节点到新数组中

*/voidtransfer(Entry[] newTable,booleanrehash){intnewCapacity = newTable.length;//遍历旧数组for(Entry e : table) {//1,首先获取数组下标元素while(null!= e) {//2.获取数组该桶位置链表中下一个元素Entry next = e.next;//3.是否需要重新该元素key的hash值if(rehash) {                e.hash =null== e.key ?0: hash(e.key);            }//4,重新确定在新数组中下标位置inti = indexFor(e.hash, newCapacity);//5.头插法:插入新链表该桶位置,若有元素,就形成链表,每次新加入的节点都插在第一位,就数组下标位置e.next = newTable[i];            newTable[i] = e;//6.继续获取链表下一个元素        e = next;        }    }}//传入容量值返回是否需要对key重新HashfinalbooleaninitHashSeedAsNeeded(intcapacity){//1.hashSeed默认为0,因此currentAltHashing为falsebooleancurrentAltHashing = hashSeed !=0;//2,sun.misc.VM.isBooted()在类加载启动成功后,状态会修改为true// 因此变数在于,capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD,debug发现正常情况ALTERNATIVE_HASHING_THRESHOLD是一个很大的值,使用的是Integer的最大值booleanuseAltHashing = sun.misc.VM.isBooted() &&            (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);//3,两者异或,只有不相同时才为true,即useAltHashing =true时,dubug代码发现useAltHashing =false,booleanswitching = currentAltHashing ^ useAltHashing;if(switching) {        hashSeed = useAltHashing            ? sun.misc.Hashing.randomHashSeed(this)            :0;    }//正常情况下是返回false,即不需要重新对key哈希returnswitching;}

代码如下:

publicVput(K key, V value){//1,若当前数组为空,初始化if(table == EMPTY_TABLE) {//分析1inflateTable(threshold);    }//2,若put的key为null,在放置在数组下标第一位,索引为0位置,从该源码可知// hashMap允许 键值对 key=null,但是只能有唯一一个if(key ==null)// 分析2returnputForNullKey(value);//3,计算key的hash,这里与1.8有区别 //分析3  inthash = hash(key);// 4,确定在数组下标位置,与1.8相同inti = indexFor(hash, table.length);// 5,遍历该数组位置,即该桶处遍历for(Entry e = table[i]; e !=null; e = e.next) {        Object k;if(e.hash == hash && ((k = e.key) == key || key.equals(k))) {// 找到相同的key,则覆盖原value值,返回旧值V oldValue = e.value;            e.value = value;//该方法为空,不用看e.recordAccess(this);returnoldValue;        }    }//因为hashMap线程不安全,修改操作没有同步锁,//该字段值用于记录修改次数,用于快速失败机制 fail-fast,防止其他线程同时做了修改,抛出并发修改异常modCount++;// 6,原数组中没有相同的key,以头插法插入新的元素//分析4addEntry(hash, key, value, i);returnnull;}

分析1: HashMap如何初始化数组的,延迟初始化有什么好处?

结论: 1、1.7,1.8都是延迟初始化,在put第一个元素时创建数组,目的是为了节省内存。

初始化代码:

privatevoidinflateTable(inttoSize){// Find a power of 2 >= toSize//1.该方法非常重要,目的为了得到一个比toSize最接近的2的幂次方的数,// 且该数要>=toSize,这个2的幂次方方便后面各种位运算// 如:new HashMap(15),指定15大小集合,内部实际 创建数组大小为2^4=16// 分析见下intcapacity = roundUpToPowerOf2(toSize);//2,确定扩容阈值threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY +1);//3,初始化数组对象table =newEntry[capacity];    initHashSeedAsNeeded(capacity);}

Q:如何确保获取到比toSize 最接近且大于等于它的2的幂次方的数?

深入理解roundUpToPowerOf2方法:

privatestaticintroundUpToPowerOf2(intnumber){// assert number >= 0 : "number must be non-negative";//如果number大于等于最大值 2^30,赋值为最大,主要是防止传参越界,number一定是否非负的 returnnumber >= MAXIMUM_CAPACITY            ? MAXIMUM_CAPACITY        : (number >1) ? Integer.highestOneBit((number -1) <<1) :1;//核心在于Integer.highestOneBit((number - 1) << 1) 此处}

先抛出2个问题:

1:这个 (number - 1) << 1 的作用是什么?

2:这个方法highestOneBit肯定是为了获取到满足条件的2的幂次方的数,背后的原理呢?

结论: Integer的方法highestOneBit(i) 这个方法是通过位运算,获取到i的二进制位最左边(最高位)的1,其余位都抹去,置为0,即获取的是小于等于i的2的幂次方的数.

如果直接传入number,那么获取到的是2的幂次方的数,但是该数一定小于等于number,但这不是我们的目的;

如highestOneBit(15)=8highestOneBit(21)=16而我们是想要获取一个刚刚大于等于number的2次方的数,(number-1)<<1 因此需要先将number 扩大二倍number <<1 , 为什么需要number-1,是考虑到临界值问题,恰好number本身就是2的幂次方,如 number=16,扩大2倍后为32, highestOneBit方法计算后结果还是32,这不符合需求。

publicstaticinthighestOneBit(inti){// HD, Figure 3-1i |= (i >>1);    i |= (i >>2);    i |= (i >>4);    i |= (i >>8);    i |= (i >>16);returni - (i >>>1);}

2的幂次方二进值特点:只有最高位为1,其他位全为0

目的:将传入i的二进制最左边的1保留,其余低位的1全变为0

原理:某数二进制: 0001 ,不关心其低位是什么,以*代替,进行运算

深圳网站建设www.sz886.com

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