Fork me on GitHub

HashMap—基本结构

前言


       数组的特点是:寻址容易,插入和删除困难,数组利用下标定位,时间复杂度为O(1),插入或删除元素的时间复杂度O(n)。

       链表的特点是:寻址困难,插入和删除容易,链表定位元素时间复杂度O(n),插入或删除元素的时间复杂度O(1)。

       那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?

       HashMap是结合两者优势,这是一种折中的方案。(在java8中引入了红黑树,是对性能的更进一步优化)

 

HashMap内部结构


HashMap实际上是一个数组,数组里面的每个元素都是一个链表。每个元素在通过put方法放入HashMap中的时候,要按照如下步骤进行:

1.根据该元素自身提供的hashcode计算出散列值,该散列值就是数组的下标

2.将新元素放入该数组位置的链表中

数组定义:

  1. /**
  2.      * The table, resized as necessary. Length MUST Always be a power of two.
  3.      */
  4. transient Entry[] table; 

      这是一个数组,transient关键字告诉我们它不会参与序列化。既然是一个数组,总有数目上限,也就意味着如果存入HashMap的元素太多,导致数组大小不能够存放所有的链表的时候,数组大小必须要能够调整。

Entry是什么类型?

 

 1 static class Entry<K,V> implements Map.Entry<K,V> {
 2         final K key;
 3         V value;
 4         Entry<K,V> next;
 5         final int hash;
 6 
 7         /**
 8          * Creates new entry.
 9          */
10         Entry(int h, K k, V v, Entry<K,V> n) {
11             value = v;
12             next = n;
13             key = k;
14             hash = h;
15         }
16         ....
17         public final boolean equals(Object o) {
18             if (!(o instanceof Map.Entry))
19                 return false;
20             Map.Entry e = (Map.Entry)o;
21             Object k1 = getKey();
22             Object k2 = e.getKey();
23             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
24                 Object v1 = getValue();
25                 Object v2 = e.getValue();
26                 if (v1 == v2 || (v1 != null && v1.equals(v2)))
27                     return true;
28             }
29             return false;
30         }
31 
32         public final int hashCode() {
33             return (key==null   ? 0 : key.hashCode()) ^
34                    (value==null ? 0 : value.hashCode());
35         }
36         ....
Entry部分源码

 

这是一个HashMap类的内部静态类。实现了Map.Entry接口。接受两个模板参数K和V。key和hash一旦在构造函数中被初始化,就不可改变,并且由于有next的存在,Entry可以构成一个单向链表。

HashMap 包含如下几个构造器:

  1. HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。

  2. ashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。

  3. HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。

HashMap的基础构造器HashMap(int initialCapacity, float loadFactor)带有两个参数,它们是初始容量initialCapacity和负载因子loadFactor。

负载因子loadFactor衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

HashMap的实现中,通过threshold字段来判断HashMap的最大容量:

threshold = (int)(capacity * loadFactor);

结合负载因子的定义公式可知,threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实的负载因子。默认的的负际载因子0.75是对空间和时间效率的一个平衡选择。当容量超出此最大容量时, resize后的HashMap容量是原来容量的两倍.

 

参考

http://www.codeceo.com/article/java-hashmap-learn.html

http://blog.csdn.net/csfreebird/article/details/7347026

http://www.cnblogs.com/xwdreamer/archive/2012/05/14/2499339.html

posted @ 2016-07-12 20:34  杨藓森  阅读(3456)  评论(0编辑  收藏  举报