HashTable
- 底层是数组+链表,无论key还是value都不能为null
- synchronized同步的,线程安全,(意味着多个线程能共享hashtable)实现线程安全的方式是在修改数据时锁住整个HashTable,效率低
- hashtable不允许null(key和value)
- 初始size为11,扩容:newsize = oldsize*2+1
- 计算index方法:index = (hash& 0x7FFFFFFF) % tab。length
synchronized同步
- 意味着一次仅有一个线程能够更改hashtable。就是说任何线程在更新hashtable的时候都要获得同步锁,其他线程要等到同步锁被释放后才能再次获得同步锁更新hashtable(这样保证了安全)
HashMap
- 底层是数组+链表,可以key和value可以是null
- 线程不安全
- 初始size为16,扩容:newsize = oldsize*2,size为2的n次幂
- 扩容针对整个Map,每次扩容后,原来数组的元素依次重新计算存放位置,并重新插入
- 插入元素后才判断该不该扩容,有可能产生无效扩容(因为如果插入后扩容,当我不再继续插入元素的话,阔不阔就没意义)
- 当Map中元素超过Entry数组的75%,触发扩容操作
- 计算index:index = hash&(tab.length - 1)
- HashMap和HashTable
- HashTable同步的(线程安全),HashMap是非同步的,效率上HashMap高(不同步就高)
- HashMap允许空键和值,Hashable不允许NULL
- HashMap和TreeMap
- 在对于Map中插入,删除和定位元素操作,HashMap是最好的选择,如果需要对一个有序的key集合进行遍历,TreeMap更好
** HashMap实现原理**
-
jdk1.8之前插在头部1,jdk1.8之后插到尾部
- 实际上是一个链表散列的数据结构,即数组和链表的结合体
- 首先根据键key计算hashcode()得到数组的下标index,如果这个位置已经放了其他元素,那么这个位置将以链表存放元素,新加入的放在链头,最老的元素在链尾(数组都是链头)。如果数组该位置没有元素,就直接将元素放在数组的位置上。
- 加入数组0号来了新元素C,就entry[0]=C,C.next=B
- 每一个数组的值存放的是一个Entry类,他的属性有key(键),value(值),next。
-
HashMap和HashTable都实现了Map接口,主要区别就是线程安全,同步性,速度。剩下的基本相同。
-
HashSet的实现原理
- 底层是HashMap结构
- HashSet值存放在HashMap的键key中(set是集合,只有单元素)
- map上的Value值统一为present