前言
数组的特点是:寻址容易,插入和删除困难,数组利用下标定位,时间复杂度为O(1),插入或删除元素的时间复杂度O(n)。
链表的特点是:寻址困难,插入和删除容易,链表定位元素时间复杂度O(n),插入或删除元素的时间复杂度O(1)。
那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?
HashMap是结合两者优势,这是一种折中的方案。(在java8中引入了红黑树,是对性能的更进一步优化)
HashMap内部结构
HashMap实际上是一个数组,数组里面的每个元素都是一个链表。每个元素在通过put方法放入HashMap中的时候,要按照如下步骤进行:
1.根据该元素自身提供的hashcode计算出散列值,该散列值就是数组的下标
2.将新元素放入该数组位置的链表中
数组定义:
-
/**
-
* The table, resized as necessary. Length MUST Always be a power of two.
-
*/
-
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 包含如下几个构造器:
-
HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
-
ashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
-
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
时间: 2024-11-03 22:01:07