HashMap的源码,在jdk1.5中相比jdk1.4,改动不大,有几个方面
 
1 jdk1.5中引入了范型,在HashMap中也有体现
2 引入了另一个hash值的计算方式,不过默认是关闭状态,可以通过设置jvm的参数开启

private static int oldHash(int h) {
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
} private static int newHash(int h) { h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
} static int hash(int h) {
return useNewHash ? newHash(h) : oldHash(h);
}
// -XX:+UseNewHashFunction or -XX:+AggressiveOpts 启动新的hash计算
private static final boolean useNewHash;
static {
useNewHash = false;
}
 
3 代码的小优化

public V get(Object key) {
// key为null时,取value的过程抽离到一个方法里
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的源码,在jdk1.6中相比于jdk1.5也没什么改动,在这个版本开始,开始启用新的hash计算方式,把以前的废弃了

static int hash(int h) {

        h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
在jdk1.7中,相比与jdk1.6,变动也不是很大。
 
在jdk1.7中,HashMap的初始数组长度,没有设置了。可能出于内存有效使用的考虑。
   // 构造方法里并没有初始化数组
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;
// 数组增长阀值,先设置成数组的初始长度
threshold = initialCapacity;
init();
} public V put(K key, V value) {
// 如果数组没有初始化,开始初始化
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
....
}
 
这里引入了一个新方法inflateTable,初始化数组 
    // 初始化数组
private void inflateTable(int toSize) {
// 计算确认初始化数组的长度
int capacity = roundUpToPowerOf2(toSize);
// 重新给数组长度的阀值赋值
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
table = new Entry[capacity];
// 这里是判断是否需要给hash值生成添加生成因子,减少hash碰撞的概率
initHashSeedAsNeeded(capacity);
} // 获得初始化数组的长度
private static int roundUpToPowerOf2(int number) {
// 分几种情况
// 1 number超过数组长度限制最大值,则返回数组长度的最大值
// 2 number小于或者等于1, 则返回1
// 3 否则返回Integer.highestOneBit((number - 1) << 1),这个表示number二进制表示时,最左边位数的值为1,其他位为0时的值
// 比如 0000 0000 0000 0000 1011 0011 0000 0000 计算后值为 0000 0000 0000 0000 1000 0000 0000 0000
return number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
} // 初始化是否使用hash值生成的生成因子
final boolean initHashSeedAsNeeded(int capacity) {
// 判断是否已经使用了生成因子,hashSeed=0表示未使用
boolean currentAltHashing = hashSeed != 0;
// 判断是否使用生成因子
boolean useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean switching = currentAltHashing ^ useAltHashing;
if (switching) {
hashSeed = useAltHashing
? sun.misc.Hashing.randomHashSeed(this)
: 0;
}
return switching;
}
 
这里在看下hash值的生成

   final int hash(Object k) {
int h = hashSeed;
// 如果key对象是字符串,则换个方式生成hash值
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
// 添加生成因子
h ^= k.hashCode(); // This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
 
上面提到一个方法Integer.highestOneBit,可能看着挺困惑的,这个是Integer里的一个方法。
    // 或者i最高位为1,其他位为0时的值
// 如:0000 0000 0000 0000 1011 0011 0000 0000 计算后值为 0000 0000 0000 0000 1000 0000 0000 0000
public static int highestOneBit(int i) {
// 开始位运算
i |= (i >> 1);// i左移1位,在和i值或运算,得到值的最高位和第2位都为1 (如果最高为到最右端不到2位,则最高位到最右端都为1)
i |= (i >> 2);// 新的i左移2位,在和i值或运算,得到值的最高位到第4位都为1 (如果最高为到最右端不到4位,则最高位到最右端都为1)
i |= (i >> 4);// 新的i左移4位,在和i值或运算,得到值的最高位到第8位都为1 ( 如果最高为到最右端不到8位,则最高位到最右端都为1)
i |= (i >> 8);// 新的i左移8位,在和i值或运算,得到值的最高位到第16位都为1 (如果最高为到最右端不到16位,则最高位到最右端都为1)
i |= (i >> 16);// 新的i左移16位,在和i值或运算,得到值的最高位到第32位都为1 (如果最高为到最右端不到32位,则最高位到最右端都为1)
// 非最高位设置为0
return i - (i >>> 1);
}
 
 

HashMap源码分析三的更多相关文章

  1. Java中HashMap源码分析

    一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtab ...

  2. JDK1.8 HashMap源码分析

      一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...

  3. Java BAT大型公司面试必考技能视频-1.HashMap源码分析与实现

    视频通过以下四个方面介绍了HASHMAP的内容 一. 什么是HashMap Hash散列将一个任意的长度通过某种算法(Hash函数算法)转换成一个固定的值. MAP:地图 x,y 存储 总结:通过HA ...

  4. Java源码解析——集合框架(五)——HashMap源码分析

    HashMap源码分析 HashMap的底层实现是面试中问到最多的,其原理也更加复杂,涉及的知识也越多,在项目中的使用也最多.因此清晰分析出其底层源码对于深刻理解其实现有重要的意义,jdk1.8之后其 ...

  5. HashMap源码分析(史上最详细的源码分析)

    HashMap简介 HashMap是开发中使用频率最高的用于映射(键值对 key value)处理的数据结构,我们经常把hashMap数据结构叫做散列链表: ObjectI entry<Key, ...

  6. 源码分析系列1:HashMap源码分析(基于JDK1.8)

    1.HashMap的底层实现图示 如上图所示: HashMap底层是由  数组+(链表)+(红黑树) 组成,每个存储在HashMap中的键值对都存放在一个Node节点之中,其中包含了Key-Value ...

  7. Java HashMap源码分析(含散列表、红黑树、扰动函数等重点问题分析)

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  8. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  9. 【JAVA集合】HashMap源码分析(转载)

    原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储 ...

随机推荐

  1. NDK学习笔记-gdb调试

    在做开发的时候,难免会crash,那么在这时候需要进行调试,在C/C++的代码调试中,gdb是很常用的gdb在这不做过多介绍,之前在C语言中已经做过总结,这里简要回顾一下 要使用gdb,在编译的时候需 ...

  2. js中的三目运算符详解

    判断 javascript中的三目运算符用作判断时,基本语法为: expression ? sentence1 : sentence2 当expression的值为真时执行sentence1,否则执行 ...

  3. [转帖]Linux-Windows 端口转发 netsh 还有 rinetd

    Linux-Windows 端口转发 https://www.cnblogs.com/operationhome/p/11284559.html 之前自己学习过 netsh 也曾经用过frp 这次学习 ...

  4. SQL SERVER创建表

    创建表 create table table_name ( column_name_1 data_type, column_name_2 data_type NOT NULL, column_name ...

  5. React—组件生命周期详解

    React—组件生命周期详解 转自 明明的博客  http://blog.csdn.net/slandove/article/details/50748473 (非原创) 版权声明:转载请注明出处,欢 ...

  6. Kafka 原理

    消息队列内部实现原理 两种消息传输方式 Kafka kafka 简介 kafka 集群角色 Kafka 工作流程分析 Kafka 生产过程分析 写入方式 分区(partition) 副本(replic ...

  7. 2019南昌网络赛  I. Yukino With Subinterval 树状数组套线段树

    I. Yukino With Subinterval 题目链接: Problem Descripe Yukino has an array \(a_1, a_2 \cdots a_n\). As a ...

  8. 17.tmux相关

    Linux终端复用神器-Tmux使用梳理 Tmux是一个优秀的终端复用软件,类似GNU Screen,但来自于OpenBSD,采用BSD授权.使用它最直观的好处就是,通过一个终端登录远程主机并运行tm ...

  9. Callable和Future的区别

    Callable 在Java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口.然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果.我们一般只能采用共享变 ...

  10. 怎样设置cookie的到期时间

    1. 使用Cookie的: Expires 属性. 它可以设置cookie的过期时间. 下面的代码表示id这条cookie的过期时间是2015年10月21日早上7点28分; Set-Cookie: i ...