HashMap与Hashtable学习笔记

(Hashtable好像不遵从波浪命名法?!!)

preview

HashMap类

定义

基于哈希表的Map接口实现。 该实现提供了所有可选的映射操作,并允许使用空值和空键。 (HashMap类大致等同于Hashtable,除了它是不同步的并且允许为空值。)这个类不能保证Map的顺序,特别是,它不能保证顺序会随着时间的推移保持不变。

HashMap结构

HashMap是由数组+单向链表+红黑树(JDK1.8新增)实现的。

数组中存储的是单向链表的头节点,单向链表中的每个节点存储的是HashMap中的元素。

在HashMap中,数组的长度为2的n次幂,这是一种非常规的设计,常规的设计是把桶的大小设计为素数。相对来说素数导致冲突的概率要小于合数,具体证明可以参考http://blog.csdn.net/liuqiyao_01/article/details/14475159,Hashtable初始化桶大小为11,就是桶大小设计为素数的应用(Hashtable扩容后不能保证还是素数)。HashMap采用这种非常规设计,主要是为了在取模和扩容时做优化,同时为了减少冲突,HashMap定位哈希桶索引位置时,也加入了高位参与运算的过程。

不过即使负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。于是,在JDK1.8版本中,对数据结构做了进一步的优化,引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法。本文不再对红黑树展开讨论,想了解更多红黑树数据结构的工作原理可以参考http://blog.csdn.net/v_july_v/article/details/6105630

  • 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。
  • 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。
  • HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。(有关ConcurrentHashMap的介绍见https://zhuanlan.zhihu.com/p/35853397
  • JDK1.8引入红黑树大程度优化了HashMap的性能。
  • HashMap 允许 key, value 为 null,同时他们都保存在第一个桶中。
  • HashMap 中 equals() 和 hashCode() 的作用:HashMap 的添加、获取时需要通过 key 的 hashCode() 进行 hash(),然后计算下标 ( n-1 & hash),从而获得要找的同的位置。当发生冲突(碰撞)时,利用 key.equals() 方法去链表或树中去查找对应的节点。

HashMap与HashTable的区别

  • HashMap不是线程安全的类,HashTable是线程安全的类。
  • HashMap允许key、value是null,HashTable不允许。
  • HashMap使用hash()方法重新计算key的hash值,HashTable直接使用key的hash值。
  • HashMap计算哈希桶索引使用位运算,HashTable使用模运算。
  • HashMap的默认容量是16,HashTable是11。
  • HashMap扩容*2,HashTable扩容*2+1。

LinkedHashMap

LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。

TreeMap

TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。

猜你喜欢

转载自blog.csdn.net/weixin_38785199/article/details/82955795