HashMap知识总结

常见的数据结构

数组:长度固定,可以使用下标索引,所有元素类型一致。
列表:长度可变,可以包含重复的元素。
集合:长度可变,不能放置重复的元素。
堆栈:只允许对最后插入的元素进行操作。后进先出,通过仅有的peek(),push(),和pop()几个方法的强制性限制达到。
队列:先进先出,通过只提供peek(),offer()和poll()这几个方法来访问数据进行限制来达到的。
链表:链表是一种由多个节点组成的数据结构,每个节点包含有数据以及指向下一个节点的引用,在双向链表里还会有一个指向前一个节点的引用。
或许,可以用一张图直观的展现集合这个大家庭。
在这里插入图片描述

HashMap底层实现

由上图可知,HashMap实现了Map接口。其底层数据结构是哈希表,而hash表其实就是链表+数组(+红黑树 jdk8之后加入)。表现形式大致如下图:
在这里插入图片描述
初始化的表长度为什么是16:

 之所以初始长度为16,目的是为了让hash既有数组快速查询优点,又能融合链表方便快捷增加删除元素的优势。

哈希表使用数组+链表的缺点:(两个极端)

 1.每次计算hash值都是同一个值,如果每次计算的hash值都是同一个,会造成链表中长度过长的问题。
 2.每次计算hash值都是不同的值,如果每次计算的hash值都不一样,会造成HashMap中数据不断扩容,造成容量不断增大。
为了解决这些问题,jdk1.8中使用红黑树代替了链表。

HashMap线程安全实现

线程安全实现上目前能考虑到 HashTable、ConcurrentHashMap以及Collections中的静态方法SynchronizedMap对HashMap进行的封装这三个方面。以及线程安全性,同步(synchronization),速度 这三个关注点。

HashTable 理解

HashTable底层的实现方式也是通过hash表的方式去实现的。HashTable是同步(synchronization)的,这意味着HashTable是线程安全的,多个线程可以共享一个HashTable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。HashMap存储键值对,调用put()向map中添加元素,使用Key计算hasncode,5.使用唯一的键获取对象。

小问题1:HashMap和Hashtable的异同?
同:
	1.底层的实现方式都是通过hash表的方式去实现。
	2.都实现了Map接口。

异:
	1.HashMap非同步,可以接受null值(null的键值(key)和值(value)),Hashtable则不行。
	2.HashMap线程不安全,HashTable线程安全。
	3.HashMap的迭代器(Iterator)是fail-fast迭代器,而HashTable的enumerator迭代器不是fail-fast的,所以当有其他线程改变了HashMap的结构,增加或者移除元素,将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出异常,这个地方是否会发生,需要看jvm,这也是两个迭代器的区别。
	4.HashMap单线程环境下比HashTable要快。
	5.HashMap不能保证随着时间的推移Map中元素次序不发生改变。

刚才提到了fail-fast迭代器,来理解一下。

fail-fast 机制是Java集合(Collection)中的一种错误机制,当多个线程对同一个集合的
内容进行操作时就可能会产生fail-fast事件。
eg:
当A B两个线程操作一个集合,在线程A通过Iterator遍历集合的过程中,该集合的内容被B线
程改变,线程A访问集合时,就会抛出ConcurrentModificationException异常,产生
fail-fast事件

ConcurrentHashMap 理解

Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

 小问题2:Hashtable和ConcurrentHashMap的区别
 同:
 	1.二者都是线程安全的。

 异:
    1.Hashtable中的key值不能为null值,ConcurrentHashMap中的key和value值都
    不能为null值。
	2.HashTable使用synchronized来锁住整张Hash表来实现线程安全,每次锁住整张表
	让线程独占。ConcurrentHashMap使用了锁分离技术,使用多个锁来控制对Hash表不同
	部分的操作,ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,(锁分段)
	每个段其实就是一个小的HashTable,他们有自己的锁,只要多个修改发生在不同的段上,
	他们就可以并发进行。

ConcurrentHashMap采用了分段技术,对于一些需要跨段的方法,如size(),containsValue() 怎样保证线程安全呢。

先按顺序锁定所有段,操作完毕后,再按顺序释放所有段的锁,按顺序很重要,否则很大可能
会出现死锁,在ConcurrentHashMap内部,段数组是final的,并且其成员变量实际上也是
final的。但是仅仅将数组声明为final 并不能保证数组成员也是final的,需要实现上的保
证,可以确保不会出现死锁,因为获得锁的顺序是固定的。
这里复习一下死锁产生必须满足的几种特定条件:
	1.互斥条件,进程对于所分配到资源具有排他性,一个资源只能被一个进程占用,直到该进程释放
	2.请求和保持条件,一个进程因请求被占用资源而发生阻塞的时候,对已经获得的资源保持不放。
	3.不可剥夺条件,任何一个资源在没被该进程释放之前,任何其他进程都无法对它剥夺占用。
	4.循环等待条件,当发生死锁时,所等待的进程必然形成一个环路,造成永久阻塞。

Collections中的静态方法SynchronizedMap

这个方法在日常项目开发中没遇到过。翻了一下源码。在Collections工具类中有一个静态内部类SynchronizedMap,该内部类中存在一个map和一个对象排斥锁mutex,在调用new synchronizedMap()方法的时候,如果传入mutex参数,就将对象排斥锁赋值为传入的对象。如果没有,就将对象排斥锁赋值为this,也就是调用synchronizedMap的实例对象,创建出synchronizedMap之后,在操作该map时,就会将方法上锁,从而实现线程安全。
在这里插入图片描述
在这里插入图片描述
提到集合工具类Collections,集合框架的工具类。里面定义的都是静态方法。

还有很多需要补充的地方,有的写的理解并不到位。有了理解再来更新修改。
子曰:温故而知新,更上一层楼!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值