試着實現一個更好的計數器.能夠對輸入的List
進行計數.java
最終實現版本使用泛型,使得能夠對任意對象進行技術,可是在編寫過程當中,先以String爲例.bash
那麼計數
這個行爲的輸入值是List<String>
,輸出值爲Map<String,Integer>
. 這裏不是強行要求Integer
的,只要可以標識數量便可.多線程
直接隨手寫一個:學習
HashMap<String,Integer> c = new HashMap<>();
stringList.forEach(per->{
c.put(per, c.getOrDefault(per, 0) + 1);//步驟1
});
return c;
}
複製代碼
這裏面有幾個點:測試
先解決第一個問題,封裝一個可變的Integer類或者使用AtomicInteger. 在沒有多線程的要求下,本身封裝一個:優化
public static final class MutableInteger {
private int val;
public MutableInteger(int val) {
this.val = val;
}
public int get() {
return this.val;
}
public void set(int val) {
this.val = val;
}
public String toString() {
return Integer.toString(val);
}
}
複製代碼
對於每個字符串,都須要get確認,而後put新值,這明顯是不科學的.this
HashMap
的put
方法,實際上是有返回值的,會返回舊值.url
這就意味着咱們能夠經過一次map操做來達到目的.spa
通過這樣兩次的優化,如今的方法爲:線程
public static Map<String, MutableInteger> count2(List<String> strings) {
HashMap<String, MutableInteger> c = new HashMap<>();
strings.forEach(per -> {
MutableInteger init = new MutableInteger(1);
MutableInteger last = c.put(per, init);
if (last != null) {
init.set(last.get() + 1);
}
});
return c;
}
複製代碼
public static void main(String[] args) {
List<String> list = new ArrayList<>();
String[] ss = {"my", "aa", "cc", "aa", "cc", "b", "w", "sssssa", "10", "10"};
for (int i = 0; i < 100000000; i++) {
list.add(ss[i % 10]);
}
long s = System.currentTimeMillis();
System.out.println(count1(list));
System.out.println(System.currentTimeMillis() - s);
long s1 = System.currentTimeMillis();
System.out.println(count2(list));
System.out.println(System.currentTimeMillis() - s1);
}
複製代碼
測試結果以下:
{aa=20000000, cc=20000000, b=10000000, w=10000000, sssssa=10000000, my=10000000, 10=20000000}
4234
{aa=20000000, cc=20000000, b=10000000, w=10000000, sssssa=10000000, my=10000000, 10=20000000}
951
複製代碼
能夠看到結果很是明顯,效率提升了4倍.
NOTE: 這個測試明顯是有偏向的,由於我這個1億條數據,只有幾種,因此數據重複率很是高.可是平常使用中數據重複率不會有這麼誇張. 可是構建1億條重複率不高的測試數據,太麻煩了.
其實起做用比較大的是可變的Integer類.
而map的操做咱們知道,取和放到時O(1)的.因此這個的提高不是特別的大.經測試,修改成兩次操做,僅增長80ms.
實現瞭如下幾個API:
List
的形式.Map<T,Integer>
package daily.counter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** * Created by pfliu on 2019/04/21. */
public class Counter<T extends Object> {
private HashMap<T, MutableInteger> c = new HashMap<>();
public void add(T t) {
MutableInteger init = new MutableInteger(1);
MutableInteger last = c.put(t, init);
if (last != null) {
init.set(last.get() + 1);
}
}
public void addAll(List<T> list) {
list.forEach(this::add);
}
public int get(T t) {
return c.get(t).val;
}
public Map<T, Integer> getAll() {
Map<T, Integer> ret = new HashMap<>();
c.forEach((key, value) -> ret.put(key, value.val));
return ret;
}
public static final class MutableInteger {
private int val;
MutableInteger(int val) {
this.val = val;
}
public int get() {
return this.val;
}
void set(int i) {
this.val = i;
}
}
}
複製代碼
固然你徹底不用本身實現,網上一大把已經實現的.
可是本身思考一下爲何要這樣實現,仍是有不少的好處的.
以上皆爲我的所思所得,若有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文連接。
聯繫郵箱:huyanshi2580@gmail.com
更多學習筆記見我的博客------>呼延十