Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
从上图我们可以发现哈希表是由数组+链表组成的,在一个长度为16的数组中,每个元素存储在链表的一个结点中,这些元素的具体存储位置通常是按照hash(key)^(length-1)来获得。例如91^15=11,155^15=11,171^15=11,所以91、155、171都存储在数组下标为11的位置。
Java中的HashMap
首先是哈希的插入,我们可以看JDK1.6中HashMap插入方法的源代码:
public V put(K key, V value) { // 如果key为null,调用putForNullKey方法进行处理 if (key == null) return putForNullKey(value); // 对key进行再一次的hash运算 int hash = hash(key.hashCode()); // 根据hash值和table长度计算应该存放的位置 int i = indexFor(hash, table.length); // 当该下标已有数据时 for (Entry<K, V> e = table[i]; e != null; e = e.next) { Object k; // 如果key的hash值相同并且key也相同,则进行替换 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); // 返回该key的原值 return oldValue; } } modCount++; // 添加新的Entry addEntry(hash, key, value, i); return null; }
代码中用到实现了Map.Entry接口的内部类Entry<K, V>,其实就是一个key-value对,里面有四个属性,分别为key、value、hash、next。
每当插入键值对为添加一个新的Entry时,会检查是否需要扩容
void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K, V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K, V>(hash, key, value, e); //检查有没有超过扩容阀值,超过则扩容 if (size++ >= threshold) resize(2 * table.length); }
扩容后链表数组长度的最大值为Integer的最大值
void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; //链表数组最大值为Integer.MAX_VALUE if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; //rehash操作 transfer(newTable); table = newTable; threshold = (int) (newCapacity * loadFactor); }
重构链表数组的具体操作如下
void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; for (int j = 0; j < src.length; j++) { Entry<K, V> e = src[j]; //检查下标为j的数组是否存在链表 if (e != null) { src[j] = null; do { Entry<K, V> next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; //将链表当前结点的元素放在新数组中 newTable[i] = e; //处理下一个结点 e = next; } while (e != null); } } }
可能不少人会有这么个疑问:要是一次性往一个HashMap中插入上千乃至上万个键值对的,那不是需要进行多次哈希表的重构,岂不是很浪费性能?
Java中用Map map = new HashMap()初始化一个HashMap的时候,默认的数组长度为16,实际上当HashMap中的键值对超过16*0.75=12个时,就会扩容。
//默认初始容量 static final int DEFAULT_INITIAL_CAPACITY = 16; //默认装载因子 static final float DEFAULT_LOAD_FACTOR = 0.75f; //链表数组 transient Entry[] table; //键值对的数量 transient int size; //扩容阀值 int threshold; //装载因子 final float loadFactor; public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); }
同时Java也提供了一个可以指定容量大小和装载因子的初始化方法,Map map = new HashMap(10000,0.8f);不过用此方法初始化出的HashMap容量不是10000,而是8192
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); // Find a power of 2 >= initialCapacity int capacity = 1; //链表数组的大小为2的n次方,便于取余后确定key所在的数组下标 while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; //阀值为容量*装载因子 threshold = (int) (capacity * loadFactor); //实例出一个该容量的Entry数组 table = new Entry[capacity]; init(); }
相关推荐
Java HashMap原理分析 Java HashMap是一种基于哈希表的数据结构,它的存储原理是通过将Key-Value对存储在一个数组中,每个数组元素是一个链表,链表中的每个元素是一个Entry对象,Entry对象包含了Key、Value和指向...
HashMap是Java编程语言中最常用的集合类之一,它属于`java.util`包,提供了一种以键值对形式存储数据的数据结构。HashMap的核心在于其高效的数据查找、...阅读“HashMap原理.pdf”文件,可以获得更深入的细节和示例。
### HashMap原理详解 #### 一、HashMap简介与应用场景 HashMap是Java集合框架中一个非常重要的组成部分,它提供了基于键值对(key-value)映射的高效数据存储方式。由于其内部采用了数组加链表(以及红黑树优化)的...
一线大厂BATJ面试题讲解-hashmap原理实现
在深入探讨HashMap的实现原理之前,我们需要了解两个关键的接口方法:`hashCode()`和`equals()`。 根据《Effective JAVA》的建议,当重写`equals()`方法时,也应重写`hashCode()`方法。这是因为在HashMap中,`...
HashMap原理的深入理解 HashMap是基于哈希表的Map接口的非同步实现,提供了所有可选的映射操作,并允许使用null值和null键。HashMap储存的是键值对,HashMap很快。此类不保证映射的顺序,特别是它不保证该顺序恒久...
要理解HashMap的工作原理,首先需要了解它如何结合数组和链表的特点来提升性能。数组的特点是查找速度快,因为它们在内存中是连续分布的,这使得通过下标访问特定元素的时间复杂度是O(1)。但数组的缺点在于其大小是...
hashMap基本工作原理,图解分析,基础Map集合
详细介绍了hashMap原理,值得一看,对于面试者有很大帮助
Java HashMap原理及实例解析 Java HashMap是一种常用的数据结构,它提供了快速的查找、插入、删除操作。HashMap的键值对的存储方式是通过键值对来存储数据的,键是唯一的,不可以重复的,而值可以重复。 HashMap的...
本教程特点: 1.更适合零基础学员: ·自Java语言起源始,循序渐进,知识点剖析细致且每章配备大量随堂练习,让你步步为营,学得透彻、练得明白 ·拒绝晦涩难懂的呆板教学,宋老师语言生动幽默,举例形象生动深入浅...
HashMap底层原理.md
### CAS操作原理 CAS操作包含三个参数:内存地址V、预期值A和新值B。如果内存地址V的值等于预期值A,则将V的值更新为新值B,整个过程是原子性的。如果V的值不是A,则操作失败,不会进行任何修改。Java中的`...
文章目录一.HashMap是什么二.HashMap继承类对比分析三.HashMap源码相关单词含义四.HashMap如何确定哈希桶数组索引位置五. HashMap 的 put 方法分析六.HashMap扩容机制七.HashMap线程安全性 一.HashMap是什么 ...
HashMap的底层原理主要依赖于哈希表,这是一种数据结构,它通过计算键的哈希码来实现高效的查找操作。 在HashMap中,每个元素都是一个键值对,存储在一个Entry对象中。当向HashMap添加键值对时,首先会计算键的哈希...
对HashMap 源码逐行进行注释,带你深入理解HashMap原理,使面试不在困难,
hashMap基本工作原理,图解分析,基础Map集合
HashMap是Java中非常常见的一种数据结构,主要用于存储键值对,其核心原理是通过哈希算法将键映射到数组中的位置来实现快速访问。本文将详细介绍HashMap的底层原理,包括其内部实现结构、关键字段的作用、以及JDK ...
在Java编程语言中,集合框架是开发者日常工作中不可或缺的一部分,HashMap作为其中的重要成员,它的实现原理对于理解Java性能优化和数据结构有深远的意义。HashMap是一个基于哈希表的数据结构,它实现了Map接口,...