有序性 线程安全性 备注**
1.HashMap 无序 不安全
2.Hashtable 无序 安全 不允许null
3.LinkedHashMap 有序 不安全 根据插入/访问顺序排序,有序的
4.ConcurrentHashMap 无序 安全 线程安全的HashMap
:keySet、entrySet
keySet
先获取所有键的集合, 再根据键获取对应的值.
entrySet
先获取map中的键值关系封装成一个个的entry对象, 存储到一个Set集合中,再迭代这个Set集合, 根据entry获取对应的key和value。
向集合中存储自定义对象 .
HashMap : 内部结构是哈希表,不是同步的。允许null作为键,null作为值。
TreeMap : 内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序。
哈希表是由数组+链表组成的,(注意,这是jdk1.8之前的)数组的默认长度为16。
为什么是数组+链表?
数组对于数据的访问如查找和读取非常方便,链表对于数据插入非常方便。
链表可以解决hash值冲突(即对于不同的key值可能会得到相同的hash值)
数组里每个元素存储的是一个链表的头结点。而组成链表的结点其实就是hashmap内部定义的一个类:Entity
Entity包含三个元素:key,value和指向下一个Entity的next。
HashMap的存储–put : null key总是存放在Entry[]数组的第一个元素
元素需要存储在数组中的位置。先判断该位置上有没有存有Entity,没有的话就创建一个Entity
HashMap的读取–get : 先定位到数组元素,再遍历该元素处的链表.
覆盖了equals方法之后一定要覆盖hashCode方法,原因很简单,比如,String a = new String(“abc”); String b = new String(“abc”); 如果不覆盖hashCode的话,那么a和b的hashCode就会不同。
HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。
也是一个散列表,它存储的内容是键值对(key-value)映射。
Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口
Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。
Hashtable中的映射不是有序的。
Hashtable继承于Dictionary类,实现了Map接口。Map是"key-value键值对"接口,Dictionary是声明了操作"键值对"函数接口的抽象类。
(1)继承的父类不同
Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。
(2)线程安全性不同
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。
(3)是否提供contains方法
HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因为contains方法容易让人引起误解。
Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
(4)key和value是否允许null值
其中key和value都是对象,并且不能包含重复key,但可以包含重复的value。
Hashtable中,key和value都不允许出现null值。
HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
(5)两个遍历方式的内部实现上不同
Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
(6)hash值不同
哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
(7)内部实现使用的数组初始化和扩容方式不同
Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是 old*2+1。
HashMap中hash数组的默认大小是16,而且一定是2的指数。
HashMap 是一个无序的 Map,因为每次根据 key 的 hashcode 映射到Entry数组上,所以遍历出来的顺序并不是写入的顺序。
因此 JDK 推出一个基于 HashMap 但具有顺序的 LinkedHashMap 来解决有排序需求的场景。
LinkedHashMap继承于HashMap 所以一些 HashMap 存在的问题 LinkedHashMap也会存在,比如不支持并发等
实现
hashcode实现唯一,双向链表实现有序
LinkedHashMap 的排序方式有两种:
根据写入顺序排序。
根据访问顺序排序。
顺序访问
每次 get 都会将访问的值移动到链表末尾,这样重复操作就能得到一个按照访问顺序排序的链表。
Hashtable的put和get方法均为synchronized的是线程安全的。
将HashMap默认划分为了16个Segment,减少了锁的争用。
写时加锁读时不加锁减少了锁的持有时间。
volatile特性约束变量的值在本地线程副本中修改后会立即同步到主线程中,保证了其他线程的可见性。
value外,其他的属性都是final的,value是volatile类型的,都修饰为final表明不允许在此链表结构的中间或者尾部做添加删除操作,每次只允许操作链表的头部。