为了更好的理解HashMap线程不安全的根源,这里提供了HashMap的简易实现:

package map.test;
import org.apache.commons.lang3.StringUtils; /**
* Author:yepei@meituan.com
* Date:2017/7/5
* Time:15:45
* ------------------------------------
* Desc:
*/
public class HashMp {
private transient Node[] entries;//数组
private transient float loadFactor;
private transient int size; public static void main(String[] args) {
HashMp map = new HashMp(2, 0.75f);
map.put(3, "A");
System.out.println(map);
map.put(5, "B");
map.put(7, "C");
map.put(11, "D");
map.put(9, "E");
map.put(12, "F");
map.put(17, "G");
map.put(22, "H");
map.put(24, "I");
map.put(25, "I");
System.out.println(map); String s1 = map.get(22);
String s2 = map.get(25);
String s3 = map.get(0);
} public HashMp(int capacity, float loadFactor) {
this.entries = new Node[capacity];
this.loadFactor = loadFactor;
} public HashMp() {
this(16, 0.75f);
} public String put(int key, String value) {
Node n = new Node(key, value);
//算Hash值,为简单起见,hash值
int i = n.getIndex(entries.length);
Integer thisKey = key;
//查找是否有重复: hashCode相等,且equals方法相等的
for (Node old = entries[i]; old != null; old = old.next) {
Integer oldKey;
//hash相同,且equals返回true,证明有重复值插入,直接替换掉旧值
if (old.getHash() == n.getHash() && ((oldKey = old.key) == key || thisKey.equals(oldKey))) {
String oldValue = old.value;
old.value = value;//替换成新值
return oldValue;//返回旧值
}
}
//该key不存在,需要增加一个结点
addEntry(n);
return null;
} public String get(int key) {
int idx = new Node(key, null).getIndex(entries.length);
Integer thisKey = key;
//查找是否有重复: hashCode相等,且equals方法相等的
for (Node old = entries[idx]; old != null; old = old.next) {
Integer oldKey = old.key;
if (thisKey == oldKey || thisKey.equals(oldKey)) {
return old.value;
}
}
return null;
} private void addEntry(Node n) {
int oldLen = entries.length;
int targetIdx = n.getIndex(oldLen);
n.next = entries[targetIdx];
entries[targetIdx] = n;//插入到链首
if (size++ >= oldLen * loadFactor) {
resize(2 * oldLen);
}
} private void resize(int newCapacity) {
Node[] old = entries;
Node[] newMap = new Node[newCapacity];
for (int j = 0; j < old.length; j++) {
Node e = old[j];
if (e == null) {
continue;
}
old[j] = null;//老表中的元素置为Null //注意:该过程会将同一个bucket中的元素逆置,因为总是将元素插在了新表的头部。
//但不是就地逆置,因为将元素逐个copy到了新的链表中
do {
Node next = e.next;//暂存尾链表(去掉head的链表)
int i = e.getIndex(newCapacity);//得到将在新表中存储的位置
e.next = newMap[i];//切断指针,重新指向新表的表头(可能为null)
newMap[i] = e;//将e插入到新链表头部
e = next;//重新将e赋值为尾链表的头,对尾链表继续“切断指针,插入新表头”
} while (e != null);
}
entries = newMap;
} @Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
int i = 0;
for (Node n : entries) {
sb.append(i++).append("{").append(n).append("}, ");
}
return StringUtils.removeEnd(sb.toString(), ", ") + "]";
} static class Node {
int key;
String value;
Node next; Node(int key, String value) {
this.key = key;
this.value = value;
} int getIndex(int tableLength) {
return getHash() % tableLength;
} //为方便起见,hash值直接取该对象的key
int getHash() {
return key;
} @Override
public String toString() {
return "(" + key + "," + value + ")——>" + next;
}
}
}

  

参考

HashMap源码解读:http://www.xiaomager.com/category/program/java/hashmap

Hashcode生成原理:http://www.cnblogs.com/godtrue/p/6395098.html

HashMap存在的三大并发问题:https://my.oschina.net/xianggao/blog/393990

简易HashMap实现的更多相关文章

  1. HashMap内部结构及实现原理

    简单介绍 在研究HashMap之前,我们先大概了解下其他数据结构在新增,查找等基础操作执行性能 数组:采用一段连续的存储单元来存储数据.对于指定下标的查找,时间复杂度为O(1):通过给定值进行查找,需 ...

  2. Java面试题之HashMap阿里面试必问知识点,你会吗?

    面试官Q1:你用过HashMap,你能跟我说说它的数据结构吗? HashMap作为一种容器类型,无论你是否了解过其内部的实现原理,它的大名已经频频出现在各种互联网Java面试题中了.从基本的使用角度来 ...

  3. hashmap的简易实现,基本实现PUT GET

    p.p1 { margin: 0; font: 12px Menlo; color: rgba(79, 118, 203, 1) } /*简易版的HASHMAP包括基本的GET  PUT思想 * 从数 ...

  4. 【简易版】HashMap(增删改查)

    1.HashMap概述 (1)首先HashMap是基于哈希表的Map接口实现的.另外HashMap中存储的数据是按照键值跟键值对的关系来进行存储的. (2)不同于ArrayList方法的是,Array ...

  5. 自己用HashMap来模拟一个Session缓存(简易版)

    本文记录:Hibernate中一级缓存的特点. 一级缓存的细节什么操作会向一 1.级缓存放入数据 save,update,saveOrUpdate,load,get,list,iterate,lock ...

  6. 实现一个简易的HashMap

    实现一个键的类型为int,值的类型为int的HashMap 输入一个T,表示操作次数: 之后每行接一个操作,可以包括插入.删除.修改.查询.清空.判断是否有这个键: 因为是刚学完随手敲的,所以功能粗糙 ...

  7. 【面向对象版】HashMap(增删改查)

    前言: 关于什么是HashMap,HashMap可以用来做些什么,这些定义类的描述,请参照[简易版]HashMap(增删改查)的内容. 这章节主要是面向实例,直接进行HashMap(增删改查)的演示. ...

  8. Spring + iBatis 的多库横向切分简易解决思路

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  9. Android学习之路——简易版微信为例(二)

    1 概述 从这篇博文开始,正式进入简易版微信的开发.深入学习前,想谈谈个人对Android程序开发一些理解,不一定正确,只是自己的一点想法.Android程序开发不像我们在大学时候写C控制台程序那样, ...

随机推荐

  1. Unix下cp、tar、sudo命令的使用

    环境 Ubuntu14.04 (这里用这样一个类Unix系统来代替Unix,sudo命令也是linux下的一个命令) 实例 : Ubuntu firefox flash插件的安装 到Adobe下载ta ...

  2. OpenVswitch mirror 镜像功能

    # 从int-br-eth1进入的包镜像一份给dummy0 # 现象:dummy0 可以抓到 int-br-eth1 进入的包 modprobe dummy ip link set up dummy0 ...

  3. 弦论(tjoi2015,bzoj3998)(sam(后缀自动机))

    对于一个给定长度为\(N\)的字符串,求它的第\(K\)小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串\(S\) 第二行为两个整数\(T\)和\(K\),\(T\)为0则表示不同 ...

  4. *p++与(*p)++与*(p++)------自增运算符常见误区

    自增运算符(++) 自增\自减运算符分为前缀形(++a)和后缀形(a++),这里重点分析自增 大部分人对前缀和后缀的理解一般是,前缀形式是先++再使用(先变后用),后缀形式是先使用再++(先用后变) ...

  5. maven项目报错--Cannot change version of project facet Dynamic Web Module to 3.0 Error in Eclipse

    错误原因: 使用ecplise构建的maven骨架默认支持的是web2.3的版本,当使用这个创建3.0版本的web项目时则会报这样的错误: Cannot change version of proje ...

  6. HTML+纯JS制作音乐播放器

    该篇文章会教你通过JavaScript制作一个简单的音乐播放器.包括播放.暂停.上一曲和下一曲. 阅读本文章你需要对HTML.CSS和Javascript有基本的了解. 话不多说,先上图. emmm. ...

  7. HTML+Javascript制作拼图小游戏详解(二)

    上一篇我们说了网页的基本布局.接下来将为大家带来具体的实现方法. 拼图通表格来实现,做一个方形的表格,改变其大小使之如图所示. 试想一下如果我们将一张图片剪成6张分别放入对应位置,然后再把它打乱,这样 ...

  8. pg_stat_statements跳过的坑

    pg_stat_statements跳过的坑 原本以为只是一个简单的插件扩展安装,三下五除二就能搞定,结果搞了很久也没找到问题所在.首先pg_stat_statements已经安装成功,且已经能够使用 ...

  9. js实现仿华为手机计算器,兼容电脑和手机屏幕

    效果图: 电脑端: 手机端: 源码: <!DOCTYPE html> <html lang="en"> <head> <meta char ...

  10. PHP相关异常

    1. Maximum execution time of 30 seconds exceeded 报错一:内存超限,具体报错语句忘了,简单说一下解决办法 1> 利用循环分批导入: 2> 每 ...