一、IntHashMap
1.1 準備
先從官網下載jar包:javasoft-collection.jar,解壓後將jar包build到Java項目中.
1.2 IntHashMap類圖
1.3 IntHashMap流程圖
從上面類圖可以看出IntHashMap和HashMap一樣都是基於Map接口,在Map中最常用的2個方法是put()和get()方法。大家都知道Map是從鍵到值的映射,每個鍵不能出現重複且每個鍵最多隻能映射到一個值。那麼IntHashMap是如何保證鍵的唯一性?可能大家會想IntHashMap的鍵是int類型,使用==來比較,這樣子雖然能保證鍵的唯一。但是隨着元素的增加,每次都進行比較效率會越來越低。什麼樣的數據結構能快速的存儲元素?答案是數組。在HashMap中是通過hash計算出bucketIndex位置找到數組中對應的元素,那麼IntHashMap呢?IntHashMap亦如此,唯一不同的是計算bucketIndex的算法。通過indexFor()方法拿到bucketindex後。它會先去數組中找這個位置上的元素IntEntry<V>是否存在,如果存在的話,再通過key去查找value,然後將新值替換掉舊value。
代碼清單2如下:
-
-
-
-
protected int indexFor(int key, int length) {
-
key += ~(key << 9);
-
key ^= (key >>> 14);
-
key += (key << 4);
-
key ^= (key >>> 10);
-
return key & (length-1);
-
}
代碼清單3如下:
-
int i = indexFor(key, table.length);
-
-
for (IntEntry<V> e = table[i]; e != null; e = e.next) {
-
if (e.key == key) {
-
V oldValue = e.value;
-
e.value = value;
-
e.recordAccess(this);
-
return oldValue;
-
}
-
}
二、IntHashMap與HashMap比較
2.1 運行效率比較
創建一個測試類,分別往IntHashMap和HashMap各插入1萬和5萬條數據來測試它們性能、GC和內存使用
-
package com.lll.operator;
-
-
import java.util.HashMap;
-
import java.util.Map;
-
-
import ch.javasoft.util.intcoll.IntHashMap;
-
-
public class ShiftTest {
-
-
-
HashMap<Integer,String> map = new HashMap<Integer,String>();
-
-
public void add()
-
{
-
for (int i = 0; i < 10000;i++) {
-
for(int j = 0;j<50000;j++)
-
{
-
if(map.get(j) == null)
-
{
-
map.put(j, "小毛驢");
-
}
-
}
-
}
-
}
-
public static void main(String[] args) {
-
long curTime = System.currentTimeMillis();
-
ShiftTest shiftTest = new ShiftTest();
-
shiftTest.add();
-
long curTime2 = System.currentTimeMillis();
-
System.out.println("耗時:"+(curTime2-curTime)+"ms");
-
try {
-
Thread.sleep(5000);
-
} catch (InterruptedException e) {
-
-
e.printStackTrace();
-
}
-
}
-
-
}
2.2 Visual GC比較
HashMap:
IntHashMap:
2.3 結果分析
10000條數據測試結果:
Map類型 |
第一次取樣 |
第二次取樣 |
第三次取樣 |
GC |
IntHashMap |
795ms |
815ms |
807ms |
NO GC |
HashMap |
866ms |
927ms |
861ms |
11.978ms |
50000條數據測試結果:
Map類型 |
第一次取樣 |
第二次取樣 |
第三次取樣 |
GC時間 |
IntHashMap |
5166ms |
4817ms |
4997ms |
NO GC |
HashMap |
4388ms |
4430ms |
3876ms |
40.453ms |
從上面的測試結果可以看出,HashMap會隨着容器大小的變化效率明顯變慢。也許從數據測試結果來看使用IntHashMap在性能上比HashMap並沒有太大優勢甚至效率還要低些,但是從GC上來看明顯IntHashMap更有優勢。那麼是什麼讓他們產生這樣的差異?
2.4 差異一
HashMap在插入元素過程中會在堆中產生大量的Integer實例(如下圖-Profiler界面),參考代碼清單4。而IntHashMap不一樣,它是以int作爲key值類型(見代碼清單5),能夠減少Integer實例的產生,減少GC負擔。
Profiler界面
代碼清單4:
-
static class Entry<K,V> implements Map.Entry<K,V> {
-
final K key;
-
V value;
-
Entry<K,V> next;
-
int hash;
-
-
-
-
-
Entry(int h, K k, V v, Entry<K,V> n) {
-
value = v;
-
next = n;
-
key = k;
-
hash = h;
-
}
代碼清單5:
-
public static class IntEntry<VV> implements IntMap.IntEntry<VV> {
-
protected final int key;
-
protected VV value;
-
protected IntEntry<VV> next;
-
-
-
-
-
protected IntEntry(int k, VV v, IntEntry<VV> n) {
-
value = v;
-
next = n;
-
key = k;
-
}
2.5 差異二
在遍歷時,IntHashMap(代碼清單6)沒有對hash進行比較。
代碼清單6
-
public V get(int key) {
-
int i = indexFor(key, table.length);
-
IntEntry<V> e = table[i];
-
while (true) {
-
if (e == null)
-
return null;
-
if (e.key == key)
-
return e.value;
-
e = e.next;
-
}
-
}
HashMap遍歷代碼清單7
-
final Entry<K,V> getEntry(Object key) {
-
int hash = (key == null) ? 0 : hash(key);
-
for (Entry<K,V> e = table[indexFor(hash, table.length)];
-
e != null;
-
e = e.next) {
-
Object k;
-
if (e.hash == hash &&
-
((k = e.key) == key || (key != null && key.equals(k))))
-
return e;
-
}
-
return null;
-
}
文章如有錯誤之處,還請見諒並能給予指正!
本博客中未標明轉載的文章歸作者
小毛驢所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。