【学习笔记-集合】HashMap 源码浅析

/**

* HashMap主要方法解析,jdk1.7版本的HashMap

* 一、构造

* 4个构造相对之前的jdk版本功能基本不变,但是代码封装更完善。

* 构造前一个参数是容量,相当于数组大小,后一个是负载因子

*/

public HashMap(int initialCapacity, float loadFactor) {

//当初始容量<0,抛出异常非法的参数容量

if (initialCapacity < 0)

throw new IllegalArgumentException("Illegal initial capacity: " +

initialCapacity);

//初始容量不能大于最大容量值,最大容量值为MAXIMUM_CAPACITY = 1 << 30;

//左移一位相当于乘以2,所以左移30位相当于2^30.

if (initialCapacity > MAXIMUM_CAPACITY)

initialCapacity = MAXIMUM_CAPACITY;

//负载因子不为空并且<=0

if (loadFactor <= 0 || Float.isNaN(loadFactor))

throw new IllegalArgumentException("Illegal load factor: " +

loadFactor);

//保存参数并且初始化数组

this.loadFactor = loadFactor;

threshold = initialCapacity;

//此初始化将插入数据,主要使用Entry

init();

}

//无参构造默认容量是16,负载因子0.75

public HashMap() {

this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);

}

//指定容量参数,默认负载因子0.75

public HashMap(int initialCapacity) {

this(initialCapacity, DEFAULT_LOAD_FACTOR);

}

//包含map的HashMap

public HashMap(Map<? extends K, ? extends V> m) {

this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,

DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);

//如果容量不够,扩大table数组

inflateTable(threshold);

//将map中的元素添加到HashMap中

putAllForCreate(m);

}

/**

* 二、创建数据链

* 静态内部类,包含键值,节点next和hash值,由于他的存在,才会让table数组项以链表方式存在

*/

static class Entry<K,V> implements Map.Entry<K,V> {

final K key;

V value;

Entry<K,V> next;

int hash;

//添加新条目

Entry(int h, K k, V v, Entry<K,V> n) {

value = v;

next = n;

key = k;

hash = h;

}

public final K getKey() {

return key;

}

public final V getValue() {

return value;

}

public final V setValue(V newValue) {

V oldValue = value;

value = newValue;

return oldValue;

}

/**

*  三、存

*  实现快速存取

*  添加数据

*/

public V put(K key, V value) {

//如果数组为空,添加数组容量

if (table == EMPTY_TABLE) {

inflateTable(threshold);

}

//如果key为空,保存null在table的第一个位置,所以HashMap可以为null

if (key == null)

return putForNullKey(value);

//计算hash值

int hash = hash(key);

//计算key的hash值在table中的位置(索引)

int i = indexFor(hash, table.length);

//从i迭代e,找到key保存的位置

for (Entry<K,V> e = table[i]; e != null; e = e.next) {

Object k;

//判断该链上是否有hash(key)值相同的情况,若存在,则将其value值覆盖,保留新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++;

//将key,value值添加在i处

addEntry(hash, key, value, i);

return null;

}

//都是添加Entry,这个是HashMap实际容量超过容器容量,下面的方法是没超过的情况

void addEntry(int hash, K key, V value, int bucketIndex) {

//当HashMap大小不小于容器大小,并且数组bucketIndex位置不为null,改变HashMap大小,记录索引

if ((size >= threshold) && (null != table[bucketIndex])) {

resize(2 * table.length);

hash = (null != key) ? hash(key) : 0;

bucketIndex = indexFor(hash, table.length);

}

//否则调用createEntry方法

createEntry(hash, key, value, bucketIndex);

}

//HashMap实际容量未超过默认容量或者初始化容量

void createEntry(int hash, K key, V value, int bucketIndex) {

//保存bucketIndex的所在位置到e中

Entry<K,V> e = table[bucketIndex];

//设置bucketIndex位置元素为新Entry,并且设置e为新Entry下一个节点

table[bucketIndex] = new Entry<>(hash, key, value, e);

size++;

}

/**

*  四、取

*  实现快速存取

*  获取数据

*/

public V get(Object key) {

//若key为null,调用getForNullKey取出value

if (key == null)

return getForNullKey();

//根据key值算出hash值并取出table对应索引处的值

Entry<K,V> entry = getEntry(key);

return null == entry ? null : entry.getValue();

}

final Entry<K,V> getEntry(Object key) {

if (size == 0) {

return null;

}

//计算hash值

int hash = (key == null) ? 0 : hash(key);

//根据hash值取出table对应索引处的值

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 != null && key.equals(k))))

return e;

}

return null;

}

时间: 2024-09-30 19:28:24

【学习笔记-集合】HashMap 源码浅析的相关文章

memcached学习笔记——存储命令源码分析上

原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command函数,探究memcached客户端的set命令,解读memcached是如何解析客户端文本命令,剖析memcached的内存管理,LRU算法是如何工作等等. 解析客户端文本命令 客户端向memcached server发出set操作,memcached server读取客户端的命令,客户端的连接状态

memcached学习笔记——存储命令源码分析下篇

上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制. 本文是延续上一篇,继续分析存储命令的源码.接上一篇内存分配成功后,本文主要讲解:1.memcached存储方式:2.add和set命令的区别. memcached存储方式 哈希表(HashTable) 哈希表在实践中使用的非常广泛,例如编译器通常会维护的一个符号表来保存标记,很多高级语言中也显式的支持哈希

Hadoop学习笔记(10) ——搭建源码学习环境

Hadoop学习笔记(10) ——搭建源码学习环境 上一章中,我们对整个hadoop的目录及源码目录有了一个初步的了解,接下来计划深入学习一下这头神象作品了.但是看代码用什么,难不成gedit?,单步调试呢? 看程序不能调那多痛苦啊,想看跟踪一下变量,想看一下执行路径都难. 所以这里,我们得把这个调试环境搭建起来.Hadoop的主要代码是用java编写的,所以这里就选用eclipse作为环境. Hadoop目录下,本身就可以为作eclipse的一个工程来操作,但这里我不想,我想自己来建一个工程,

Java集合---HashMap源码剖析

无论是在平时的练习还是项目当中,HashMap用的是非常的广,真可谓无处不在.平时用的时候只知道HashMap是用来存储键值对的,却不知道它的底层是如何实现的. 一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果

[转载] Java集合---HashMap源码剖析

转载自http://www.cnblogs.com/ITtangtang/p/3948406.html 一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collection

HashMap源码浅析

HashMap源码主要一些属性 //默认的初始化容量(2的n次方) static final int default_inital_capacity = 16; //最大指定容量为2的30次方 static final int maximum_capacity = 1 << 30; //默认的加载因子 static final float default_load_factor = 0.75f; //hashmap的底层结构,entry数组 transient Entry[] table; /

java.util.HashMap源码浅析之解决hash冲突

HashMap是java无论是企业管理系统还是web或者其他应用层的程序开发,都是应用比较多的一种数据结构,正好最近面试有问到与HashMap解决hash冲突的方式(本人菜比没答上来),现浅析源码以解惑 且记录,将来在项目上尽量避免此类问题的出现,大家都知道HashMap为key-value存储,在HashMap中,HashMap本身拥有一个Entry数组,Entry则存有key-value,且对于Hashmap来讲一个key只能对应一个value     首先是put方法          

Java入门系列之集合HashMap源码分析(十四)

前言 我们知道在Java 8中对于HashMap引入了红黑树从而提高操作性能,由于在上一节我们已经通过图解方式分析了红黑树原理,所以在接下来我们将更多精力投入到解析原理而不是算法本身,HashMap在Java中是使用比较频繁的键值对数据类型,所以我们非常有必要详细去分析背后的具体实现原理,无论是C#还是Java原理解析,从不打算一行行代码解释,我认为最重要的是设计思路,重要的地方可能会多啰嗦两句. HashMap原理分析 我们由浅入深,循序渐进,首先了解下在HashMap中定义的几个属性,稍后会

iScroll学习笔记2--浅读源码

iscroll的架子是这样的 (function (window, document, Math){ var utils = (function (){ var me = {}; // 扩展一些常用的工具方法为me的方法 return me; }()); function IScroll(el, options){ // 初始化一些属性和状态 } IScroll.prototype = { constructor: IScroll, // 主体方法都在这里 } }(window, documen

Python爬虫框架Scrapy 学习笔记 7------- scrapy.Item源码剖析

在前面的example中,我们知道定义一个Item类很简单,只要继承scrapy.Item,然后添加几个类型为scrapy.Field的对象作为类属性,就像下面这样 import scrapy class Product(scrapy.Item):     name = scrapy.Field()     price = scrapy.Field()     stock = scrapy.Field()     last_updated = scrapy.Field(serializer=st