HashMap实现原理简析及实现的demo(一看就明白)
HashMap底层就是一个数组结构,数组中的每一项又是一个链表。
jdk源码:
transient Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
//.....
}
table就是一个Node类的数组,而Node类继承了Map.Entry<k,v>。每个 Map.Entry 其实就是一个键值对对,它还持有一个指向下一个元素的引用"next",这就构成了链表。如下图:

table数组的索引在逻辑上叫做“桶”(bucket),它存储了链表的第一个元素。而key的hashcode()方法用来找到Entry对象所在的桶,也就是寻找index。当两个key有相同的hash值,他们会被放在table数组的同一个桶里面。
好了,直接看HashMap的工作原理:
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
实现demo:
public class HashMapDemo<K, V> {
private class Entry<K, V> {
int hash;
K key;
V value;
Entry<K, V> next;
Entry(int hash, K key, V value, Entry<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
private static final int DEFAULT_CAPACITY = 1 << 4;
private Entry<K, V>[] table;
private int capacity;
private int size;
public HashMapDemo() {
this(DEFAULT_CAPACITY);
}
public HashMapDemo(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException();
} else {
table = new Entry[capacity];
size = 0;
this.capacity = capacity;
}
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0 ? true : false;
}
private int hash(K key) {
double tmp = key.hashCode() * (Math.pow(5, 0.5) - 1) / 2;
double digit = tmp - Math.floor(tmp);
return (int) Math.floor(digit * capacity);
}
public void put(K key, V value) {
if (key == null) {
throw new IllegalArgumentException();
}
int hash = hash(key);
Entry<K, V> nEntry = new Entry<K, V>(hash, key, value, null);
Entry<K, V> entry = table[hash];
while (entry != null) {
if (entry.key.equals(key)) {
entry.value = value;
return;
}
entry = entry.next;
}
nEntry.next = table[hash];
table[hash] = nEntry;
size++;
}
public V get(K key) {
if (key == null) {
throw new IllegalArgumentException();
}
int hash = hash(key);
Entry<K, V> entry = table[hash];
while (entry != null) {
if (entry.key.equals(key)) {
return entry.value;
}
entry = entry.next;
}
return null;
}
public static void main(String[] args) {
HashMapDemo<String, String> map = new HashMapDemo<String, String>();
map.put("1", "11");
map.put("1", "22");
map.put("3", "33");
System.out.println(map.get("1"));
System.out.println(map.get("3"));
}
}

面试小问:“如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?”除非你真正知道HashMap的工作原理,否则你将回答不出这道题。默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。
-------------------待完善
HashMap实现原理简析及实现的demo(一看就明白)的更多相关文章
- Java Annotation 及几个常用开源项目注解原理简析
PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...
- [转载] Thrift原理简析(JAVA)
转载自http://shift-alt-ctrl.iteye.com/blog/1987416 Apache Thrift是一个跨语言的服务框架,本质上为RPC,同时具有序列化.发序列化机制:当我们开 ...
- Java Android 注解(Annotation) 及几个常用开源项目注解原理简析
不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...
- PHP的错误报错级别设置原理简析
原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...
- Spring系列.@EnableRedisHttpSession原理简析
在集群系统中,经常会需要将Session进行共享.不然会出现这样一个问题:用户在系统A上登陆以后,假如后续的一些操作被负载均衡到系统B上面,系统B发现本机上没有这个用户的Session,会强制让用户重 ...
- SIFT特征原理简析(HELU版)
SIFT(Scale-Invariant Feature Transform)是一种具有尺度不变性和光照不变性的特征描述子,也同时是一套特征提取的理论,首次由D. G. Lowe于2004年以< ...
- 基于IdentityServer4的OIDC实现单点登录(SSO)原理简析
写着前面 IdentityServer4的学习断断续续,兜兜转转,走了不少弯路,也花了不少时间.可能是因为没有阅读源码,也没有特别系统的学习资料,相关文章很多园子里的大佬都有涉及,有系列文章,比如: ...
- ARP攻击原理简析及防御措施
0x1 简介 网络欺骗攻击作为一种非常专业化的攻击手段,给网络安全管理者,带来严峻的考验.网络安全的战场已经从互联网蔓延到用户内部的网络, 特别是局域网.目前利用ARP欺骗的木马病毒在局域网中广泛传 ...
- EM算法原理简析——图解
一. 扯淡 转眼间毕业快一年了,这期间混了两份工作,从游戏开发到算法.感觉自己还是喜欢算法,可能是大学混了几年算法吧!所以不想浪费基础... 我是个懒得写博客的人,混了几年coding,写的博客不超过 ...
随机推荐
- Windws Server 2008 R2 WEB环境配置之IIS7/IIS7.5+FastCGI+PHP 5.6.4+MYSQL+phpMyAdmin
本篇为WEB环境配置的汇总篇,其中PHP以FASTCGI方式来运行,这种方式性能更高.经过配置后,我们的服务器将同时可以运行PHP和.NET的程序,属称全能服务器.所有配置可以根据自身实际需要进行增减 ...
- Poco::Crypto--加解密(AES)
Poco::Crypto--加解密(AES) 1 int main(const std::vector<std::string>& args) 2 { 3 /*TO DO*/ 4 ...
- Swift 学习- 08 -- 闭包
// 闭包是自包含的函数代码块, 可以在代码中被传递和使用, swift 中的闭包 与 C语言 和 OC 中的代码块 (blocks) 以及其他一些编程语言中的匿名函数比较类似 // 闭包可以捕获和存 ...
- Modbus库开发笔记之三:Modbus TCP Server开发
在完成了前面的工作后,我们就可以实现有针对性的应用了,首先我们来实现Modbus TCP的服务器端应用.当然我们不是做具体的应用,而是对Modbus TCP的服务器端应用进行封装以供有需要时调用. 这 ...
- oracle_基本SQL语言
一:DDL数据定义语言 1:create(创建) 创建表 CREATE TABLE <table_name>( column1 DATATYPE [NOT NULL] [P ...
- LeetCode(113):路径总和 II
Medium! 题目描述: 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径. 说明: 叶子节点是指没有子节点的节点. 示例:给定如下二叉树,以及目标和 sum = ...
- java----面对对象
面对对象: public class Demo { public static void main(String[] args){ Horse h = null; h = new Horse(); / ...
- requests中get和post传参
get请求 get(url, params=None, **kwargs) requests实现get请求传参的两种方式 方式一: import requests url = 'http://www. ...
- 制作linux下的.run安装包
前言 之前往linux上安装一个软件,都是以压缩包或者压缩包+shell的方法,这每次安装,都是先scp到某个目录, 解压,安装......稍微厉害的,会写个shell脚本.但是还是达不到真正的快速方 ...
- Vue注意事项及用得较多的属性归纳
1.prop是一个对象而不是字符串数组时,它包含验证要求.props: { // 基础类型检测 (`null` 意思是任何类型都可以) propA: Number, // 多种类型 propB: [S ...