例子:我們經常會碰到統計一個文檔中的字符串出現的次數這樣的問題,在這樣的問題中會創建一個這樣的map來存放數據:
Map<String, Integer>map = new HashMap<String, Integer>();
那麼對於下一個字符串String x=”abc”;
一般情況下要做的操作爲:
int count = map.containsKey(x)?map.get(x):0;
map.put(x, count++);
首先查看map中是否已經包含該字符串,再獲得該字符串對應的value值,其實根據java api的get()方法,可以省略containsKey()操作,get()方法在map中:
- 包含該key值時,返回對應的value值;
- 不包含該key值時,返回null對象。
故上述操作可以改成:
Integer count = map.get(x);
if(count == null)
map.put(x, 1);
else
map.put(x, ++count);
這樣就省去了一次查找map的過程。
最壞進行兩次map操作,並使用MutableInteger 代替Integer類型對象
但是這樣的代碼在每次更新key值對應的value值時,會創建新的Integer對象來代替原無效的Integer對象,這樣就會導致原無效Integer對象需要等待一個GC週期,並在下一個週期被回收。但是Integer爲 immutable類型,那麼是否可以使用一個MutableInteger如下,來代替Integer類型的value對象呢?
public class MutableInteger {
private int val;
public MutableInteger(int val) {
this.val = val;
}
public int get() {
return val;
}
public void set(int val) {
this.val = val;
}
}
用該MutableInteger 對象代替Integer類型的value值,故有如下代碼操作:
public void incrementCount(String key){
MutableInteger count = map.get(key);
if(null == count){
map.put(key, new MutableInteger(1));
}else
count.set(count.get()+1);
}
那麼,在最壞情況下(String字符串爲第一次出現),需要兩次map操作:get(),和put()操作。
並且在每次更新value值時,不需要反覆創建Integer值,並將無效原value值等待GC回收。而是獲得map中key值對應的value的可變對象MutableInteger的引用,然後更新堆內存上的value對象中的成員變量。
只是用一次map操作,就可以更新value值:
查看java api會發現其中的put()方法:
- 若key值存在,則返回對應的value值,
- 否則,返回null對象。
那麼,是否可以使用MutableInteger來將map中更新value的操作減少到一次呢?
如下代碼:
public void incrementCount(String key){
MutableInteger tmpCount = new MutableInteger(1);
MutableInteger oldCount = map.put(key, tmpCount);
if(null != oldCount)
tmpCount.set(oldCount.get()+1);
}
在這個實現中,配合MutableInteger類型對象的使用(基於java的引用傳遞)可以將map中value的更新操作減少到一次,但是也有一個無效對象需要等待GC回收的問題。
可以說這種方法是基於空間換時間。而上述第二種方法是時間換空間。
參考文獻:
1,Most efficient way to increment a Map value in Java — Only search the key once