面试知识点-HashMap

要点

  1. HashMap基于哈希表的 Map 接口的实现。HashMap不是线程安全的。
  2. HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
  3. HashMap中hash数组的默认大小是16,而且一定是2的指数。Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。HashMap 实现 Iterator,支持fast-fail。
  4. 哈希表是由数组+链表组成的,它是通过把key值进行hash来定位对象的,这样可以提供比线性存储更好的性能。

 

我们知道,数据结构的物理存储结构只有两种:顺序存储结构和链式存储结构(像栈,队列,树,图等是从逻辑结构去抽象的,映射到内存中,也这两种物理组织形式),而在上面我们提到过,在数组中根据下标查找某个元素,一次定位就可以达到,哈希表利用了这种特性,哈希表的主干就是数组。

  比如我们要新增或查找某个元素,我们通过把当前元素的关键字 通过某个函数映射到数组中的某个位置,通过数组下标一次定位就可完成操作。

        存储位置 = f(关键字)

  其中,这个函数f一般称为哈希函数,这个函数的设计好坏会直接影响到哈希表的优劣。举个例子,比如我们要在哈希表中执行插入操作:

先通过哈希函数计算出实际存储地址,然后从数组中对应地址取出即可。

 

哈希冲突

  然而万事无完美,如果两个不同的元素,通过哈希函数得出的实际存储地址相同怎么办?也就是说,当我们对某个元素进行哈希运算,得到一个存储地址,然后要进行插入的时候,发现已经被其他元素占用了,其实这就是所谓的哈希冲突,也叫哈希碰撞。前面我们提到过,哈希函数的设计至关重要,好的哈希函数会尽可能地保证 计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突。

 

那么哈希冲突如何解决呢?

  哈希冲突的解决方案有多种:

  1. 开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),
  2. 散列函数法
  3. 链地址法

而HashMap即是采用了链地址法,也就是数组+链表的方式

【hashcode方法不能确保唯一性,所以不同key值它们的hashcode可能一致,会导致通过indexFor方法最后算出来的数组table下标一致,二个元素放在一个位置,就用上链表表了,通过next,一直往后加,你看get方法,从table中取出来的值也是用循环去找具体的元素的,因为有可能找到来的那个元素实际上只是那个位置上的元素,有可能这个位置上有多个元素。】

 

参考文章:

http://www.javashuo.com/article/p-kjwdvdbc-ke.html