hashhMap
# 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的更多相关文章
随机推荐
- leetcode-154-寻找旋转排序数组中的最小值
题目描述: 方法一: class Solution: def findMin(self, nums: List[int]) -> int: left, right = 0, len(nums) ...
- configparser 配置文件模块
#_author:star#date:2019/11/7# configparser 配置文件模块import configparserconfig=configparser.ConfigParser ...
- 记录一次dubbo不能正常抛出特定异常
BUG场景 今天同事的代码中出现一个问题,让我帮忙排查一下.原代码大致如下 dubbo服务消费者: @Resource private IPayWayService payWayService; @R ...
- 莫烦PyTorch学习笔记(五)——分类
import torch from torch.autograd import Variable import torch.nn.functional as F import matplotlib.p ...
- 使用movable-view制作可拖拽的微信小程序弹出层效果。
仿了潮汐睡眠小程序的代码.[如果有侵权联系删除 最近做的项目有个弹出层效果,类似音乐播放器那种.按照普通的做了一般感觉交互不是很优雅,设计妹子把潮汐睡眠的弹层给我看了看,感觉做的挺好,于是乘着有空仿照 ...
- 19-11-06-&
你&我处于这里……在一起? $$\text{%%%Wearry}$$ ZJ: 一遇到Wearry的思维题就得×得够呛. 考试心态炸裂,码上三个暴力然后就不知道该干啥了. 现在就想敲自己. 不要 ...
- phpstorm中完成一键快速注释函数头
先保存函数,再在函数头写/**+enter就行了 /** * @param $num1 * @param $num2 * @param $opt * @return float|int */ func ...
- Python数据挖掘之决策树DTC数据分析及鸢尾数据集分析
Python数据挖掘之决策树DTC数据分析及鸢尾数据集分析 今天主要讲述的内容是关于决策树的知识,主要包括以下内容:1.分类及决策树算法介绍2.鸢尾花卉数据集介绍3.决策树实现鸢尾数据集分析.希望这篇 ...
- Myeclipse 10使用hibernate生成注解(annotation)实体类
以MySQL数据库为例,请在数据库里面建好对应的表. 1.配置数据库链接 打开Myelipse Database Explorer视图 Window-->Open Perspective--&g ...
- mongdb 使用聚合函数异常
异常信息: Command execution failed: Error [The 'cursor' option is required, except for aggregate with t ...