前言
平时经常使用HashMap,也大致了解它的数据结构,但是一直没有一窥究竟,今天正好有时间看了下源码(确实有很多自己不懂以及可学习的地方),现在简单做些笔记
1.HashMap的数据结构
上图大致描绘了HashMap的数据结构,其基本算法如下:
当我们把一个元素a放入HashMap时,首先获取a的hashCode,然后通过hash算法得到哈希值k,最后把k散列到数组位置i上,如果该位置为空则直接插入,否则遍历这个位置上的链表(若找到相同的节点(equals方法)则更新节点,否则插入)
2.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);
// capacity(数组容量)总是为2的幂次方,如果你传入的initialCapacity为17,capacity会被初始化为32
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
// 阀值 = 容量 * 负载因子(当map中元素个数超过阀值时,会自动做一次扩容以及rehash)
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
public HashMap() {
// 默认负载因子为0.75
this.loadFactor = DEFAULT_LOAD_FACTOR;
// 默认阀值为 12 (16*0.75)
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
// 默认容量为16
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
3.HashMap中put方法的实现
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
// 下面两步通过hash得到key,并散列到数组位置i上
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
// 循环遍历下标为i的链表
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
// 若当前key在链表中存在,则update value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue; // return oldValue
}
}
// 若当前key在链表中不存在,则插入
modCount++;
addEntry(hash, key, value, i);
return null;
}
我们再看下addEntry方法:
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);
// 如果此时元素个数达到map的阀值,则resize->rehash
if (size++ >= threshold)
resize(2 * table.length);
}
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
// 若原来数组容量已经为MAXIMUM_CAPACITY,则此次仅仅扩大容量至Integer.MAX_VALUE,而不做rehash
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
// rehash
transfer(newTable);
table = newTable;
// 重新计算map的阀值
threshold = (int)(newCapacity * loadFactor);
}
最后看一下传说中的rehash
void transfer(Entry[] newTable) {
// newTable是新的数组(扩容过),src是老的数组
Entry[] src = table;
int newCapacity = newTable.length;
// 循环每个数组下表所对应的链表做rehash
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[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);
}
}
}
4.HashMap中的get方法
public V get(Object key) {
if (key == null)
return getForNullKey();
// 通过hash得到key在数组中index,然后循环遍历
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;
}
5.简单总结:
1.capacity:table容量,它的值总是2的幂次方,所以即使你设置初始化容量为17,它的也会自动扩容到32;
2.threshold:HashMap中元素个数阀值,当超过这个阀值的时候,HashMap会自动扩容一倍,然后对其中的元素做一次rehash,所以为了避免HashMap反复的做rehash(这个严重会影响hashmap的性能),我们应该在使用前给出一个合理的初始容量(通常这个又是很难做到的);
3.loadFactor:负载因子,刚才我说了阀值threshold的作用,但是这个值是如何确定的呢,它等于:capacity * loadFactor;
4.hashCode方法与equals方法:通常我们看到一些文章都会说,如果你重写了equals方法一定也要重写hashCode方法,反之亦然,但是为什么要这样呢? 其实我觉的这两个方法本身没有多大的关系,但是因为Set,Map这些集合的关系,这两个方法被紧密的绑定在一起;在Set和Map集合中,当我们判断两个key或者两个元素是否相同的时候,首先比较它们的hashcode值是否相同,若不相同,则判定这两个元素不相同,否则再进行equals方法的比较;若重写了equals方法而未重写hashcode方法,那么即使你认为是相同的元素对于Set和Map来讲也是不同的,并且当你设计一些公用类的时候,你无法预测别人是否会将这些类作为元素放入Set和Map中,所以建议最好重写hashCode和equals方法;
5. 遗留问题:代码中一些java关键字的学习;hash算法;一些内部类譬如EntrySet
- 大小: 13.9 KB
分享到:
相关推荐
Java集合专题总结:HashMap和HashTable源码学习和面试总结 本文总结了Java集合专题中的HashMap和HashTable,涵盖了它们的源码学习和面试总结。HashMap是一种基于哈希表的集合类,它的存储结构是一个数组,每个元素...
JAVA之hashmap源码分析 Mobile-Dev-Analysis Analysis of android or java 红岩网校工作站移动开发部学员分组学习 为了让大家学的更加坚固,采取小组学习的方式帮助大家学习,同时在学习研究的过程中需要不断的做...
hashmap源码 learning-record - 学习轨迹记录 10月1号 7月18号 基础算法:反转单向链表 7月16号 7月11号 7月9号 复习 : HashTable和HashMap的区别详解 LeetCode 27. 删除元素(Remove Element) 7月8号 7月5号 复习...
总结一下,电话本管理系统使用HashMap实现的优势在于其快速的查找和操作性能。通过理解和运用HashMap的机制,我们可以构建出一个高效、易维护的电话本系统。在实际开发中,了解和掌握HashMap的内部原理对于优化代码...
总结起来,理解HashMap的put方法源码对于优化Java程序中的数据处理至关重要,它揭示了HashMap如何高效地管理数据并处理冲突,这对于开发者来说是非常宝贵的编程知识。通过深入学习这些细节,我们可以更好地利用...
JSONObject是JSON数据结构中的一个核心概念,它代表了键值对的集合,类似于Java中的HashMap。在JSON中,键必须是字符串,而值可以是JSON支持的任何类型,如字符串、数字、布尔值、数组、对象或其他JSON对象。例如: ...
### Struts 源码学习知识点详解 #### 一、Struts框架简介 Struts是一个开源的MVC(Model-View-Controller)框架,用于Java Web应用开发。它简化了Web应用程序的开发流程,提供了清晰的结构化设计,使得开发者能够...
HashMap总结、面试资料!!(2020下半年) 包含:HashMap线程安全 + ConcurrentHashMap + HashMap + 源码分析 + jdk1.8的HashMap和ConcurrentHashMap。 欢迎下载学习
源码学习 equals 编码规范 注解-未总结 :spider_web:前端 :revolving_hearts:数据结构和算法 CH LFU LRU :woman_biking:网络 :hollow_red_circle:操作系统 Linux :information:数据库 Oracle MYQL 事务 - 未学习 ...
总结来说,JDK源码学习是提升Java开发技能的必经之路,"JavaSourceLearn"提供了这样的学习资源和途径。通过系统地学习和实践,开发者不仅能深化对Java语言的理解,还能培养解决问题和创新的能力,这对任何Java开发者...
【JAVA学习总结】 Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems(现已被Oracle公司收购)于1995年推出。它的设计目标是具有跨平台性、可移植性、安全性和高效性,使得“一次编写,到处运行”成为...
ArrayList核心源码+扩容机制分析LinkedList核心源码分析HashMap核心源码+底层数据结构分析ConcurrentHashMap核心源码+底层数据结构分析LinkedHashMap核心源码分析CopyOnWriteArrayList核心源码分析...
描述中提到的“21-23”可能是指三个视频教程的章节,分别深入分析了HashMap的源码、HashMap的内部结构总结和TreeMap。 首先,Collection接口是Java集合框架的根接口,它定义了对一组对象进行操作的基本方法,如添加...
项目相关 项目介绍 使用建议 贡献指南 常见问题 ...HashMap 核心源码+底层数据结构分析 ConcurrentHashMap 核心源码+底层数据结构分析 LinkedHashMap 核心源码分析 CopyOnWriteArrayList 核心源码分析
【标题】:“(转)Java学习总结” 这篇“Java学习总结”显然是一位程序员在学习Java编程语言过程中的心得体会。Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems(现为Oracle公司的一部分)于1995年...
源码学习对于理解编程语言的工作原理、提高编程技能以及学习最佳实践至关重要。通过阅读和分析源码,开发者可以深入理解类、对象、方法、接口等概念,学习如何组织代码结构,以及如何利用Java库和框架来解决问题。 ...
描述中提到“学习java 总结的代码”,这表明这些源码可能是作者在学习Java过程中编写的示例程序或练习项目,对于初学者来说极具参考价值。它们可以帮助初学者理解Java语法、类和对象的概念、异常处理、数据结构、...
HashMap(JDK1.8)源码+底层数据结构分析 ConcurrentHashMap 源码+底层数据结构分析 IO IO 基础知识总结 IO 设计模式总结 IO 模型详解 并发 知识点/面试题总结 : (必看 ) Java 并发常见知识点&面试题总结(上) Java ...
HashMap(JDK1.8)源码+底层数据结构分析 ConcurrentHashMap 源码+底层数据结构分析 IO IO 基础知识总结 IO 设计模式总结 IO 模型详解 并发 知识点/面试题总结 : (必看 ) Java 并发常见知识点&面试题总结(上) Java ...
面试题总结部分,Java开发者应该掌握的基本概念包括:多线程(线程安全、同步机制)、集合框架(List、Set、Map的区别与选择)、异常处理、设计模式(单例、工厂、装饰器等)、JVM内存模型以及垃圾回收机制。...