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 ...
随机推荐
- 【代码笔记】iOS-评分系统(小星星)
一,效果图. 二,工程图. 三,代码. RootViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additi ...
- 【读书笔记】iOS网络-应用间通信
一,URL方案 URL方案有3个主要用途:根据设备上其他应用的存在与否调整逻辑,切换到其他应用以及响应打开你的应用的其他应用.你还可以通过URL方案从某个站点或是在基于Web的认证流程结束是打开应用. ...
- UITableView传值(自己使用)(属性,代理传值)
今天有些匆忙. 效果图如下: 代码如下: #import <UIKit/UIKit.h> #import "FirstViewController.h" @interf ...
- 大数据计算平台Spark内核解读
1.Spark介绍 Spark是起源于美国加州大学伯克利分校AMPLab的大数据计算平台,在2010年开源,目前是Apache软件基金会的顶级项目.随着 Spark在大数据计算领域的暂露头角,越来越多 ...
- OBST(Optimal Binary Tree最优二叉搜索树)
二叉搜索树 二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的 ...
- OSGI.NET 框架浅析
关于osgi.net ,想必大家也听说过,以下是自己在学习osgi.net 过程中整理出来的内容,供大家学习参与使用. 1. UIOSP 开放工厂框架架构 开放工厂所有插件基于OSGi.NET面向服 ...
- HDFS
1.HDFS shell 1.0查看帮助 hadoop fs -help <cmd> 1.1上传 hadoop fs -put <linux上文件> <hdfs上的路径& ...
- SQL SERVER同步环境新增发布对象时不能生成(sp_MS+表名)同步存储过程
在配置了同步的用户环境(订阅端:请求订阅) 在发布端: 1.企业管理器SSMS—复制—本地发布—发布属性—项目(选中发布对象) 2.在企业管理里—查看快照代理状态(启动) 在订阅服务器: USE [D ...
- 从本地向 Github 上传项目步骤攻略(快速上手版)
最近想把之前自己做的一些好玩的项目共享到Github,网上找了一圈上传教程,都感觉写的太深奥.复杂,云里雾里,特把自己的方法纪录如下: PS:这种方式一般适用于:开始做项目时,没有直接在github上 ...
- JDK环境变量详细讲解
首先,你应该已经安装了 java 的 JDK 了,笔者安装的是:jdk-8u65-windows-x64 下载地址: http://www.oracle.com/technetwork/java/ja ...