一开始,注释说明就讲了一个我们经常问的问题:hashmap和hashtable有什么区别:
* the HashMap class is roughly equivalent toHashtable,exceptthat it is unsynchronized and permits nulls.
HashMap 和Hashtable基本上是一样的,但是HashMap不是线程安全,并且HashMap是允许null(key,value可以为null),反过来说,就是Hashtable是不能放null进去。
一、构造函数
HashMap有4个构造函数
HashMap()
Constructs an empty
HashMap with the default initial capacity (16) and the default load factor (0.75)
.
构造一个初始容积为16,负荷率为0.75的空的HashMap
|
HashMap(int initialCapacity)
Constructs an empty
HashMap with the specified initial capacity and the default load factor (0.75).
构造一个自定义容积和负荷率为0.75的空的HashMap
|
HashMap(int initialCapacity, float loadFactor)
Constructs an empty
HashMap with the specified initial capacity and load factor.
构造一个自定义容积和负荷率的空的HashMap
|
HashMap(Map<? extendsK,? extends V> m)
Constructs a new
HashMap with the same mappings as the specified
Map.
复制一个已有的Map
|
容量(capacity)很好理解,就是哈希表的大小,如果不知道什么是哈希表的,可以查一下相关资料,
负荷率(load factor)就是当一张表达到多少使用率的时候,会自动增加哈希表的长度,此时哈希表内部数据结构重建。
先看HashMap(int initialCapacity, float loadFactor)这个方法,主要就是初始化loadFactor(记录负荷率)和threshold(记录容量×负荷率)
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();
}
然后再看一下HashMap(Map<? extends K,? extends V> m)
public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
inflateTable(threshold);
putAllForCreate(m);
}
inflateTable后面讲put函数的时候再说,主要看一下putAllforCreate()
private void putAllForCreate(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
putForCreate(e.getKey(), e.getValue());
}
</pre><pre name="code" class="java"><pre name="code" class="java"> private void putForCreate(K key, V value) {
int hash = null == key ? 0 : hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
e.value = value;
return;
}
}
createEntry(hash, key, value, i);
}
其中,indexFor函数就是获取该哈希值在 table中的位置,具体可以看看 http://www.360doc.com/content/10/0505/19/495229_26234886.shtml,讲得比较详细
不过又可以得看出,如果map中传的一个引用类型,如果用上面的方法构造另外一个map,改变其中一个都会影响另外一个.
二、put()
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
<span style="white-space:pre"> </span>//如果table[i]存在
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
<span style="white-space:pre"> </span> //首先hash值相同,然后再比较是不是==或者equals,如果相同则<span style="font-size: 12px;">赋予</span>新的value值
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
<span style="white-space:pre"> </span>//如果不存在,则在该位置增加一个Entry<K,V>
addEntry(hash, key, value, i);
return null;
}
在addEntry()中,首先第一步是判断是不是需要扩容,即当前table使用量(用size来记录)是否>=threshold,如果满足则会调用resize()函数,resize()就是对已有的table进行扩容(扩大2倍),然后再对已有的元素重新分配
三、inflateTable(int p)
private static int roundUpToPowerOf2(int number) {
// assert number >= 0 : "number must be non-negative";
int rounded = number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (rounded = Integer.highestOneBit(number)) != 0
? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded
: 1;
return rounded;
}
这个函数用是来生成不小于一个数的 2的幂整数,其中有2个函数是比较经典的位运算
public static int highestOneBit(int i) {
// HD, Figure 3-1
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
可以看成,通过不断右移,把高位第一个出现1的位置后面的全部弄成1,然后再用这个数减去它右移一位的数
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
其他的方法就比较简单了, get 函数就是找出key 的哈希值,再在哈希表里面取出对应的Entity(链表),再遍历链表寻找对应的value