Java集合系列:-----------08HashMap的底层实现
对于HashMap感觉一直是看了忘,忘了看。这里就单独写一篇日志来记录一下。HashMap的底层实现。
非常好的讲HashMap的博客:http://blog.csdn.net/vking_wang/article/details/14166593
我们看一下HashMap的底层结构:

我们看到底层是一个数组,数组里面存放的是Entry,Enrty是什么?
我们看一下源代码:
//很明显这是一个单向链表,next是只想下一个Entry的
//Key就是我们键值
//value是我们的值
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash; /**
* Creates new entry.
*/
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 final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
} public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
} public final String toString() {
return getKey() + "=" + getValue();
} /**
* This method is invoked whenever the value in an entry is
* overwritten by an invocation of put(k,v) for a key k that's already
* in the HashMap.
*/
void recordAccess(HashMap<K,V> m) {
} /**
* This method is invoked whenever the entry is
* removed from the table.
*/
void recordRemoval(HashMap<K,V> m) {
}
}
这个就是数组里面存放的Entry,里面存放了Key键值还有键值对应的value。
我们思考的问题是,HashMap是怎么把key和value放进去的,我们常用的就是:
HashMap hashmap=new HashMap();
hashmap.put(key,value);
其实就是根据传进来的key的值计算hash值,根据hash值确定要放在数组的哪个位置。比如放在table[2];
先去table[2]这个位置找Entry,Entry是一个链表,遍历这个链表,发现这个链表中有key的值相等与传进来的key相等那么之前的值就被替换掉,并且把老的值返回。
对应的源代码是:
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
但是如果我们在table[2]这个位置看到本身就没有Entry的话,我们就要新增一个Entry。
代码如下:

我们看一下addEntry的代码:
void addEntry(int hash, K key, V value, int bucketIndex) {
//把table数组上面的Entry取出来,
Entry<K,V> e = table[bucketIndex];
//新建一个Entry,并把之前取出来的Entry挂在到这个新加入的Entry的next
//下面
table[bucketIndex] = new Entry<>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
我们再看一下HashMap是怎么取值的:
hashmap.get(Key):
代码如下:

先找到Table数组里面Entry的位置,然后从这个位置里面搜索这个Entry这个单链表,找到key对应的Entry的value。


上面三种遍历方法,给出一个例子来讲解:
import java.util.Map;
import java.util.Random;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Collection; /*
* @desc 遍历HashMap的测试程序。
* (01) 通过entrySet()去遍历key、value,参考实现函数:
* iteratorHashMapByEntryset()
* (02) 通过keySet()去遍历key、value,参考实现函数:
* iteratorHashMapByKeyset()
* (03) 通过values()去遍历value,参考实现函数:
* iteratorHashMapJustValues()
*
* @author skywang
*/
public class HashMapIteratorTest { public static void main(String[] args) {
int val = 0;
String key = null;
Integer value = null;
Random r = new Random();
HashMap map = new HashMap(); for (int i=0; i<12; i++) {
// 随机获取一个[0,100)之间的数字
val = r.nextInt(100); key = String.valueOf(val);
value = r.nextInt(5);
// 添加到HashMap中
map.put(key, value);
System.out.println(" key:"+key+" value:"+value);
}
// 通过entrySet()遍历HashMap的key-value
iteratorHashMapByEntryset(map) ; // 通过keySet()遍历HashMap的key-value
iteratorHashMapByKeyset(map) ; // 单单遍历HashMap的value
iteratorHashMapJustValues(map);
} /*
* 通过entry set遍历HashMap
* 效率高!
*/
private static void iteratorHashMapByEntryset(HashMap map) {
if (map == null)
return ; System.out.println("\niterator HashMap By entryset");
String key = null;
Integer integ = null;
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next(); key = (String)entry.getKey();
integ = (Integer)entry.getValue();
System.out.println(key+" -- "+integ.intValue());
}
} /*
* 通过keyset来遍历HashMap
* 效率低!
*/
private static void iteratorHashMapByKeyset(HashMap map) {
if (map == null)
return ; System.out.println("\niterator HashMap By keyset");
String key = null;
Integer integ = null;
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
key = (String)iter.next();
integ = (Integer)map.get(key);
System.out.println(key+" -- "+integ.intValue());
}
} /*
* 遍历HashMap的values
*/
private static void iteratorHashMapJustValues(HashMap map) {
if (map == null)
return ; Collection c = map.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}
}
}
下面的一篇博客我们就要讲解TreeMap了,TreeMap涉及到红黑树,看完代码面试那本书的二叉树那个章节在来看这方面的内容。
Java集合系列:-----------08HashMap的底层实现的更多相关文章
- Java 集合系列 15 Map总结
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 12 TreeMap
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 11 hashmap 和 hashtable 的区别
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列之一:JCF集合框架概述
容器,就是可以容纳其他Java对象的对象.Java Collections Framework(JCF)为Java开发者提供了通用的容器 java集合主要划分为四个部分: Collection(Lis ...
- Java 集合系列之二:List基本操作
1. Java List 1. Java List重要观点 Java List接口是Java Collections Framework的成员. List允许您添加重复元素. List允许您拥有'nu ...
- java集合系列之HashMap源码
java集合系列之HashMap源码 HashMap的源码可真不好消化!!! 首先简单介绍一下HashMap集合的特点.HashMap存放键值对,键值对封装在Node(代码如下,比较简单,不再介绍)节 ...
- java集合系列之LinkedList源码分析
java集合系列之LinkedList源码分析 LinkedList数据结构简介 LinkedList底层是通过双端双向链表实现的,其基本数据结构如下,每一个节点类为Node对象,每个Node节点包含 ...
- java集合系列之ArrayList源码分析
java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...
- Java 集合系列之五:Map基本操作
1. Java Map 1. Java Map 重要观点 Java Map接口是Java Collections Framework的成员.但是它不是Collection 将键映射到值的对象.一个映射 ...
- 深入java集合系列文章
搞懂java的相关集合实现原理,对技术上有很大的提高,网上有一系列文章对java中的集合做了深入的分析, 先转载记录下 深入Java集合学习系列 Java 集合系列目录(Category) HashM ...
随机推荐
- Facebook开源动画库 POP-POPBasicAnimation运用
动画在APP开发过程中还是经常出现,将花几天的时间对Facebook开源动画库 POP进行简单的学习:本文主要针对的是POPBasicAnimation运用:实例源代码已经上传至gitHub,地址:h ...
- 【代码笔记】iOS-16进制颜色与UIColor互转
一,效果图 二,工程目录. 三,代码 RootViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additio ...
- XML,DTD,XSD,XSL的区别
XML=可扩展标记语言(eXtensible Markup Language). 可扩展标记语言XML是一种简单的数据存储语言,使用一系列简单的标记描述数据,而这些标记可用 方便的方式建立,虽然XML ...
- 转换Excel表格到MarkDown:exceltk
源码和下载: 源码:https://github.com/fanfeilong/exceltk 下载:http://files.cnblogs.com/files/math/exceltk0.0.9. ...
- (视频) 《快速创建网站》 3.2 WordPress多站点及Azure在线代码编辑器 - 扔掉你的ftp工具吧,修改代码全部云端搞定
本文是<快速创建网站>系列的第6篇,如果你还没有看过之前的内容,建议你点击以下目录中的章节先阅读其他内容再回到本文. 访问本系列目录,请点击:http://devopshub.cn/tag ...
- jstree动态生成树
前篇文章简单介绍了静态生成树,这篇文章将通过后台把数据通过json形式传到前台,进行动态生成树. 本篇的程序所用框架为Spring MVC,可以很方便的通过controller层传json到前台. 前 ...
- hadoop2.4.1集群搭建
准备Linux环境 修改主机名: $ vim /etc/sysconfig/network NETWORKING=yes HOSTNAME=hadoop001 修改IP: # vim /etc/sys ...
- 【故障处理】ORA-28040: No matching authentication protocol
[故障处理]ORA-28040: No matching authentication protocol 1.1 BLOG文档结构图 1.2 前言部分 1.2.1 导读和注意事项 各位技术爱好者 ...
- asp.net服务器控件onclick带参数
服务器控件处理参数随笔 正确: <asp:CheckBox ID="cbComplareProduct" runat="server" Text=&quo ...
- 神奇的expect
想写自动化脚本的时候,遇到需要交互的,如ssh,scp,就束手无策,直到我知道了expect. expect 有一系列expect-send对组成,就像聊天一样. expect A send B ex ...