# hashMap原理 #
HashMap是一个双列集合,是线程不安全的。以key、value的形式储存值。底层是由数组+链表+红黑树组成的,数组是HashMap的主干,链表则是主要为了解决哈希冲突而存在的,根据key计算出hash值,存储在数组里面,当hsah值冲突的时候,通过equals方法比较,如果不同就创建链表存储在链表里面。当链表长度超过8的时候,会自动转化为红黑树。他的容量initialCapacity默认为16,负载因子loadFactory默认为0.75。当存储的容量大于hashmap的容量乘以0.75的时候,就会自动扩容。

我们通过hash方法计算索引,得到数组中保存的位置,看一下源码

 static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

HashMap中的hash算法是通过key的hashcode值与其hashcode右移16位后得到的值进行异或运算得到的

扩展问题:为什么扩容的时候为啥一定必须是2的多少次幂?

因为如果只有2的n次幂的情况时最后一位二进制数才一定是1,这样能最大程度减少hash碰撞。

链表数据插入方法

l HashMap 1.7使用的是头插法,扩容后位置与原链表位置相反。防止尾部遍历,不然每次插入的时候都要定位到尾部节点,如果多线程情况下容易形成环;

l HashMap 1.8使用的是尾插法,因为其中会使用到红黑树,构建红黑树有个过程,扩容后位置与原链表相同;

扩展:因为HashMap 1.7是用单链表进行的纵向延伸,当采用头插法时会容易出现逆序且环形链表死循环问题。但是在HashMap 1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。

因为新数组table下标并不是根据循环逐步递增的,而是通过(table.length-1)& hash计算得到,因此扩容后,存放的位置就可能发生变化,那么到底发生怎样的变化呢,就是由下面的算法得到.

   * 通过e.hash & oldCap来判断节点位置通过再次hash算法后,是否会发生改变,如
* 果为0表示不会发生改变,如果为1表示会发生改变。到底怎么理解呢,举个例子:
* e.hash = 13 二进制:0000 1101
* oldCap = 32 二进制:0001 0000
* &运算: 0 二进制:0000 0000
* 结论:元素位置在扩容后不会发生改变
*/
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
/**
* e.hash = 18 二进制:0001 0010
* oldCap = 32 二进制:0001 0000
* &运算: 32 二进制:0001 0000
* 结论:元素位置在扩容后会发生改变,那么如何改变呢?
* newCap = 64 二进制:0010 0000
* 通过(newCap-1)&hash
* 即0001 1111 & 0001 0010 得0001 0010,32+2 = 34
*/
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
/**
* 若(e.hash & oldCap) == 0,下标不变,将原表某个下标的元素放到扩容表同样
* 下标的位置上
*/
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
/**
* 若(e.hash & oldCap) != 0,将原表某个下标的元素放到扩容表中
* [下标+增加的扩容量]的位置上
*/
newTab[j + oldCap] = hiHead;
}
}
}

# hashMap和hashTable的区别 #

hashmap线程不安全,hashtable线程安全。
hashmap键值可以为null。
hashtable键值不可以为null。

hashMap和hashTable的实现原理区别

继承的父类不同

对外提供的接口不同

对Null key 和Null value的支持不同

线程安全性不同

遍历方式的内部实现上不同

初始容量大小和每次扩充容量大小的不同

计算hash值的方法不同

ConcurrentHashMap

1、悲观锁与乐观锁:
悲观锁是指如果一个线程占用了一个锁,而导致其他所有需要这个锁的线程进入等待,一直到该锁被释放,换句话说就是这个锁被独占,比如说典型的就是synchronized;乐观锁是指操作并不加锁,而是抱着尝试的态度去执行某项操作,如果操作失败或者操作冲突,那么就进入重试,一直到执行成功为止。

2、原子性,指令有序性和线程可见性:
这三个性质在多线程编程中是核心的问题。原子性和事务的原子性一样,对于一个操作或者多个操作,要么都执行,要么都不执行。指令有序性是指,在我们编写的代码中,上下两个互不关联的语句不会被指令重排序。指令重排序是指处理器为了性能优化,在无关联的代码的执行是可能会和代码顺序不一致。比如说int i = 1;int j = 2;那么这两条语句的执行顺序可能会先执行int j = 2;线程可见性是指一个线程修改了某个变量,其他线程能马上知道。

3、无锁算法(nonblocking algorithms):
使用低层原子化的机器指令, 保证并发情况下数据的完整性。典型的如CAS算法。

4、内存屏障:
在《深入理解JVM》中解释是:它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;它会强制将对缓存的修改操作立即写入主存;如果是写操作,它会导致其他CPU中对应的缓存行无效。在使用volatile修饰的变量会产生内存屏障。

volatile关键字

在基本清除了java内存模型之后,我们开始详细说明一下volatile关键字,在concurrentHashMap之中,有很多的成员变量都是用volatile修饰的。被volatile修饰的变量有如下特性:

①使得变量更新变得具有可见性,只要被volatile修饰的变量的赋值一旦变化就会通知到其他线程,如果其他线程的工作内存中存在这个同一个变量拷贝副本,那么其他线程会放弃这个副本中变量的值,重新去主内存中获取

②产生了内存屏障,防止指令进行了重排序,关于这点的解释,请看下面一段代码:

 public class VolatileTest {

 int a = 0; //
int b = 1; //
volatile int c = 2; //
int d = 3; //
int e = 4; // }

在如上的代码中,因为c变量是用volatile进行修饰,那么就会对该段代码产生一个内存屏障,用以保证在执行语句3的时候语句1和语句2是绝对执行完毕的,而且在执行语句3的时候,语句4和语句5肯定没有执行。同时说明一下,在上述代码中虽然保证了语句3的执行顺序不可变换,但是语句1和语句2,语句4和语句5可能发生指令重排序哦。

hashhMap的更多相关文章

随机推荐

  1. 关于JAVA项目中CLASSPATH路径详解

    写的不错:http://blog.csdn.net/cheney521/article/details/8672066 以下内容源于复制,把自己觉得不错的东西收集起来: 在dos下编译java程序,就 ...

  2. SpringBoot_Mybatis MyBatisPlus

    一.SpringBoot中使用Mybatis springBoot中使用mybatis跟以前spring中使用方法一样. 1.mybatis配置: spring: datasource: url: j ...

  3. hdu6088 组合数+反演+拆系数fft

    题意:两个人van石头剪子布的游戏一共n盘,假设A赢了a盘,B赢了b盘,那么得分是gcd(a,b),求得分的期望*\(3^{2*n}\) 题解:根据题意很明显有\(ans=3^{n}*\sum_{a= ...

  4. 【Codeforces Round #424 (Div. 2) D】Office Keys

    [Link]:http://codeforces.com/contest/831/problem/D [Description] 有n个人,它们都要去一个终点,终点位于p; 但是,在去终点之前,他们都 ...

  5. Django之深入了解路由层

    目录 ORM表关系建立 一对一 一对多 多对多 Django 请求生命周期 url 路由层 路由匹配 无名分组 有名分组 反向解析 路由匹配条件无分组的情况的反向解析 无名分组情况的反向解析 有名分组 ...

  6. 洛谷P2526 【SHOI2001】小狗散步

    原题传送门 题目背景 Grant喜欢带着他的小狗Pandog散步.Grant以一定的速度沿着固定路线走,该路线可能自交.Pandog喜欢游览沿途的景点,不过会在给定的N个点和主人相遇.小狗和主人同时从 ...

  7. Python(四)基础篇之「文件对象&错误处理」

    [笔记]Python(四)基础篇之「文件对象&错误处理」 2016-12-08 ZOE    编程之魅  Python Notes: ★ 如果你是第一次阅读,推荐先浏览:[重要公告]文章更新. ...

  8. https证书加密

    对称加密 浏览器向服务端发送请求时,服务端首先给浏览器发送一个秘钥,浏览器用秘钥对传输的数据进行加密后发送给浏览器,浏览器拿到加密后的数据使用秘钥进行解密 非对称加密 服务端通过rsa算法生成一个公钥 ...

  9. css3之背景background-origin,background-clip,background-size

    background-origin属性指定了背景图像的位置区域. content-box, padding-box,和 border-box区域内可以放置背景图像. background-clip用来 ...

  10. JZOJ5898【NOIP2018模拟10.6】距离统计

    题目 题目大意 给你带边权的树,然后有多高询问,每次询问距离某个点第kkk近的节点的距离. 思考 一眼看下去,首先就是想到如何动态的区间第K大,还要支持区间修改-- 于是想了半天,觉得不可做-- 最终 ...