对于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的底层实现的更多相关文章

  1. Java 集合系列 15 Map总结

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  2. Java 集合系列 12 TreeMap

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  3. Java 集合系列 11 hashmap 和 hashtable 的区别

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  4. Java 集合系列之一:JCF集合框架概述

    容器,就是可以容纳其他Java对象的对象.Java Collections Framework(JCF)为Java开发者提供了通用的容器 java集合主要划分为四个部分: Collection(Lis ...

  5. Java 集合系列之二:List基本操作

    1. Java List 1. Java List重要观点 Java List接口是Java Collections Framework的成员. List允许您添加重复元素. List允许您拥有'nu ...

  6. java集合系列之HashMap源码

    java集合系列之HashMap源码 HashMap的源码可真不好消化!!! 首先简单介绍一下HashMap集合的特点.HashMap存放键值对,键值对封装在Node(代码如下,比较简单,不再介绍)节 ...

  7. java集合系列之LinkedList源码分析

    java集合系列之LinkedList源码分析 LinkedList数据结构简介 LinkedList底层是通过双端双向链表实现的,其基本数据结构如下,每一个节点类为Node对象,每个Node节点包含 ...

  8. java集合系列之ArrayList源码分析

    java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...

  9. Java 集合系列之五:Map基本操作

    1. Java Map 1. Java Map 重要观点 Java Map接口是Java Collections Framework的成员.但是它不是Collection 将键映射到值的对象.一个映射 ...

  10. 深入java集合系列文章

    搞懂java的相关集合实现原理,对技术上有很大的提高,网上有一系列文章对java中的集合做了深入的分析, 先转载记录下 深入Java集合学习系列 Java 集合系列目录(Category) HashM ...

随机推荐

  1. 你真的了解UITableViewCell重用吗?

    一:首先查看一下关于UITableViewCell重用的定义 - (nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentif ...

  2. 【代码笔记】iOS-书架页面

    一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController ...

  3. 【代码笔记】iOS-评分,支持我们

    一,效果图. 二,工程图. 三,代码. RootViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additi ...

  4. 【代码笔记】iOS-将图片处理成圆的

    一,效果图. 二,工程图. 三,代码. ViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additional ...

  5. 【原】你真的懂iOS的autorelease吗?

    或许这个题目起得有点太高调了,不过我只是想纠正一些童鞋对于autorelease的认识,如果能帮到几个人,那这篇文章也就值得了!当然,高手请绕道 本文主要探讨两个方面:(1)autorelease对象 ...

  6. TFS中向源代码方案中添加文件

    一些情况下,不能使用VS提供的菜单直接将文件添加到源代码项目,例如该文件是使用TT生成的,或者依赖于其它文件     此时可以在此文件的父级依赖文件上右击,即可添加未受托管的文件     另一种方法, ...

  7. 使用UltraEdit实现从DOS文件到UNIX文件的批量转换

    最近把公司从SVN切到GIT下,因为大多同事在Windows下开发,又碰到换行符问题,找到一个批量转换方法 打开UE->在文件中替换,把^p替换成^n,然后设置好要替换的文件和路径,就开始替换吧 ...

  8. win10_x64更新错误解决: 安装一些更新时出现问题,但我们稍后会重试。如果持续出现这些问题,并且你想要搜索Web或联系支持人员以获取相关信息,以下信息可能会对你有帮助:

    可能的原因: 1.windows 服务没打开 win+r,打开[运行]对话框 输入 [service.msc] 找到 [Windows Firewall]和[Internet connection s ...

  9. msbuild ConfuserEx.Build 加密

    https://www.nuget.org/packages/ConfuserEx.Build/ <?xml version="1.0" encoding="utf ...

  10. 查看centos版本号

    --写在开始-- 玩Linux,不同的版本会有一些细微区别: so,经常需要查看服务器版本号: --正文-- 有以下命令可以查看linux服务器版本号: # lsb_release -a LSB Ve ...