日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java高效计数器

發布時間:2023/12/20 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java高效计数器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文轉載地址: ? ? ? ? ? ? ?http://blog.csdn.net/renfufei/article/details/14120775

我們經常使用 HashMap作為計數器(counter)來統計數據庫或者文本中的某些東西.
本文將使用HashMap來實現計數器的3種不同方式進行對比。

1. 新手級計數器
如果使用這一類別的計數器,那么代碼大致如下所示:
[java] view plaincopyprint?
  • String?source?=?"my?name?is?name?me?and?your?name?is?her?first?her";??
  • String[]?words?=?source.split("?");??
  • //?新手級計數器??
  • public?static?void?testNaive(String[]?words){??
  • ????HashMap<String,?Integer>?counter?=?new?HashMap<String,?Integer>();??
  • ????for?(String?w?:?words)?{??
  • ????????if(counter.containsKey(w)){??
  • ????????????int?oldValue?=?counter.get(w);??
  • ????????????counter.put(w,?oldValue+1);??
  • ????????}?else?{??
  • ????????????counter.put(w,?1);??
  • ????????}??
  • ????}??
  • } ?

  • 在每次循環中,判斷是否包含了相應的key,如果包含,那么值在原來的基礎上加1,如果沒有,那就設置為1.
    此種方式簡單又直接,但并不是很有效率。效率不高的原因如下:
    1.1 當一個key存在時,containsKey() 和 get() 分別調用了一次,這意味著對map進行了兩次查找。
    1.2 因為 Integer 是不可變的,每次循環在增加計數值的時候將會創建一個新的對象.


    2. 入門級計數器
    那么我們自然需要使用一個可變的整數來避免創建太多個Integer對象.可變整數類可以如下面所示來定義:
    [java] view plaincopyprint?
  • //?可變Integer??
  • 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);??
  • ????}??
  • } ?

  • 那么計數器可以用如下的方式來改進:
    [java] view plaincopyprint?
  • //?入門級計數器??
  • public?static?void?testBetter(String[]?words){??
  • ????HashMap<String,?MutableInteger>?counter?=?new?HashMap<String,?MutableInteger>();??
  • ????for?(String?w?:?words)?{??
  • ????????if(counter.containsKey(w)){??
  • ????????????MutableInteger?oldValue?=?counter.get(w);??
  • ????????????oldValue.set(oldValue.get()+1);?//?因為是引用,所以減少了一次HashMap查找??
  • ????????}?else?{??
  • ????????????counter.put(w,?new?MutableInteger(1));??
  • ????????}??
  • ????}??
  • } ?

  • 因為不需要創建太多的Integer對象,看起來好了一些。然而,key存在的情況下,每次循環依然要進行兩次查找.

    3. 卓越級計數器
    HashMap 的 put(key,value) 方法會返回key對應的當前value.了解這個特性,我們可以利用原有值來進行遞增,并不需要多次的查找.
    [java] view plaincopyprint?
  • public?static?void?testEfficient(String[]?words){??
  • ????HashMap<String,?MutableInteger>?counter?=?new?HashMap<String,?MutableInteger>();??
  • ????for?(String?w?:?words)?{??
  • ????????MutableInteger?initValue?=?new?MutableInteger(1);??
  • ????????//?利用?HashMap?的put方法彈出舊值的特性??
  • ????????MutableInteger?oldValue?=?counter.put(w,?initValue);??
  • ????????if(oldValue?!=?null){??
  • ????????????initValue.set(oldValue.get()?+?1);??
  • ????????}??
  • ????}??
  • } ?

  • 4. 性能差異
    為了測試這三種實現方式的性能,采用了下面的代碼。先看看結果如何,性能測試分別執行了多次,對每一個數量級的測試,誤差不算太大,所以取其中的一個結果排列如下:
    [plain] view plaincopyprint?
  • 10000000?次循環:??
  • 新手級計數器:?7726594902??
  • 入門級計數器:?6516014840??
  • 卓越級計數器:?5736574103??
  • ??
  • ??
  • 1000000?次循環:??
  • 新手級計數器:?777480106??
  • 入門級計數器:?642932000??
  • 卓越級計數器:?571867738??
  • ??
  • ??
  • 100000?次循環:??
  • 新手級計數器:?84323682??
  • 入門級計數器:?70176906??
  • 卓越級計數器:?61219664??
  • ??
  • ??
  • 10000?次循環:??
  • 新手級計數器:?13279550??
  • 入門級計數器:?7874100??
  • 卓越級計數器:?6460172??
  • ??
  • ??
  • 1000?次循環:??
  • 新手級計數器:?4542172??
  • 入門級計數器:?2933248??
  • 卓越級計數器:?992749??
  • ??
  • ??
  • 100?次循環:??
  • 新手級計數器:?3092325??
  • 入門級計數器:?1101695??
  • 卓越級計數器:?423942??
  • ??
  • ??
  • 10?次循環:??
  • 新手級計數器:?1993788??
  • 入門級計數器:?558150??
  • 卓越級計數器:?153156??
  • ??
  • ??
  • 1?次循環:??
  • 新手級計數器:?1625898??
  • 入門級計數器:?427494??
  • 卓越級計數器:?69473 ?

  • 從上面的輸出可以看到,10000次的時候, 13:8:6 秒,相差很明顯.特別是 新手級計數器和入門級計數器之間的比例,這說明創建對象是很耗資源的操作。
    當然,次數更多的差距不明顯的原因在于,觸發了多次的GC垃圾回收,同時也證明了垃圾回收的代價確實很大。

    完整的測試代碼如下:

    ?

    [java] view plaincopyprint?
  • import?java.util.HashMap;??
  • ??
  • public?class?TestCounter?{??
  • ??????
  • ????public?static?void?main(String[]?args)?{??
  • ????????//?源字符串??
  • ????????String?source?=?"my?name?is?name?me?and?your?name?is?her?first?her";??
  • ????????//?計時,單位:?微秒??
  • ????????long?startTime?=?0;??
  • ????????long?endTime?=?0;??
  • ????????long?duration?=?0;??
  • ????????//?測試次數??
  • ????????int?loop?=?1?*?10000;??
  • ??
  • ????????System.out.println(loop?+"?次循環:");??
  • ????????startTime?=?System.nanoTime();??
  • ????????testNaive(source,loop);??
  • ????????endTime?=?System.nanoTime();??
  • ????????duration?=?endTime?-?startTime;??
  • ????????System.out.println("新手級計數器:?"?+?duration);??
  • ????????//??
  • ????????startTime?=?System.nanoTime();??
  • ????????testBetter(source,?loop);??
  • ????????endTime?=?System.nanoTime();??
  • ????????duration?=?endTime?-?startTime;??
  • ????????System.out.println("入門級計數器:?"?+?duration);??
  • ????????//??
  • ????????startTime?=?System.nanoTime();??
  • ????????testEfficient(source,?loop);??
  • ????????endTime?=?System.nanoTime();??
  • ????????duration?=?endTime?-?startTime;??
  • ????????System.out.println("卓越級計數器:?"?+?duration);??
  • ????}??
  • ??
  • ????//?新手級計數器??
  • ????public?static?void?testNaive(String?source,?int?loop){??
  • ????????if(null?==?source){??
  • ????????????return;??
  • ????????}??
  • ????????//??
  • ????????String[]?words?=?source.split("?");??
  • ????????for?(int?i?=?0;?i?<?loop;?i++)?{??
  • ????????????testNaive(words);??
  • ????????}??
  • ????}??
  • ????public?static?void?testNaive(String[]?words){??
  • ????????HashMap<String,?Integer>?counter?=?new?HashMap<String,?Integer>();??
  • ????????for?(String?w?:?words)?{??
  • ????????????if(counter.containsKey(w)){??
  • ????????????????int?oldValue?=?counter.get(w);??
  • ????????????????counter.put(w,?oldValue+1);??
  • ????????????}?else?{??
  • ????????????????counter.put(w,?1);??
  • ????????????}??
  • ????????}??
  • ????}??
  • ????//?可變Integer??
  • ????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);??
  • ????????}??
  • ????}??
  • ??????
  • ????//?入門級計數器??
  • ????public?static?void?testBetter(String?source,?int?loop){??
  • ????????if(null?==?source){??
  • ????????????return;??
  • ????????}??
  • ????????//??
  • ????????String[]?words?=?source.split("?");??
  • ????????for?(int?i?=?0;?i?<?loop;?i++)?{??
  • ????????????testBetter(words);??
  • ????????}??
  • ????}??
  • ????public?static?void?testBetter(String[]?words){??
  • ????????HashMap<String,?MutableInteger>?counter?=?new?HashMap<String,?MutableInteger>();??
  • ????????for?(String?w?:?words)?{??
  • ????????????if(counter.containsKey(w)){??
  • ????????????????MutableInteger?oldValue?=?counter.get(w);??
  • ????????????????oldValue.set(oldValue.get()+1);?//?因為是引用,所以減少了一次HashMap查找??
  • ????????????}?else?{??
  • ????????????????counter.put(w,?new?MutableInteger(1));??
  • ????????????}??
  • ????????}??
  • ????}??
  • ??????
  • ????//?卓越級計數器??
  • ????public?static?void?testEfficient(String?source,?int?loop){??
  • ????????if(null?==?source){??
  • ????????????return;??
  • ????????}??
  • ????????//??
  • ????????String[]?words?=?source.split("?");??
  • ????????for?(int?i?=?0;?i?<?loop;?i++)?{??
  • ????????????testEfficient(words);??
  • ????????}??
  • ????}??
  • ????public?static?void?testEfficient(String[]?words){??
  • ????????HashMap<String,?MutableInteger>?counter?=?new?HashMap<String,?MutableInteger>();??
  • ????????for?(String?w?:?words)?{??
  • ????????????MutableInteger?initValue?=?new?MutableInteger(1);??
  • ????????????//?利用?HashMap?的put方法彈出舊值的特性??
  • ????????????MutableInteger?oldValue?=?counter.put(w,?initValue);??
  • ????????????if(oldValue?!=?null){??
  • ????????????????initValue.set(oldValue.get()?+?1);??
  • ????????????}??
  • ????????}??
  • ????}??
  • } ?

  • 當你實用計數器的時候,很可能也需要根據值來進行排序的方法,請參考: the frequently used method of HashMap.

    ?


    5. Keith網站評論列表
    我覺得最好的評論如下:

    添加了三個測試:
    1) 重構了 “入門級計數器”,不使用containsKey,改為只使用get方法. 通常你需要的元素是存在于 HashMap 中的, 所以將 2 次查找精簡為 1次.
    2) 作者 michal 提到過的方式,使用 AtomicInteger來實現 .
    3) 使用單個的int 數組來進行對比,可以使用更少的內存,參見 http://amzn.com/0748614079


    我運行了測試程序3次,并挑選出最小的那個值(以減少干擾). 注意: 你不能在程序中讓運行結果受到太多干擾,因為內存不足可能會受到gc垃圾回收器太多的影響.


    新手級計數器: 201716122
    入門級計數器: 112259166
    卓越級計數器: 93066471
    入門級計數器 (不使用 containsKey): 69578496
    入門級計數器 (不使用 containsKey, with AtomicInteger): 94313287
    入門級計數器 (不使用 containsKey, with int[]): 65877234

    入門級計數器 (不使用 containsKey 方法:):
    [java] view plaincopyprint?
  • HashMap<string,?mutableinteger="">?efficientCounter2?=?new?HashMap<string,?mutableinteger="">();??
  • for?(int?i?=?0;?i?<?NUM_ITERATIONS;?i++)??
  • for?(String?a?:?sArr)?{??
  • MutableInteger?value?=?efficientCounter2.get(a);??
  • ???
  • if?(value?!=?null)?{??
  • value.set(value.get()?+?1);??
  • }??
  • else?{??
  • efficientCounter2.put(a,?new?MutableInteger(1));??
  • }??
  • } ?
  • 入門級計數器 (不使用 containsKey, 使用 AtomicInteger):
    [java] view plaincopyprint?
  • HashMap<string,?atomicinteger="">?atomicCounter?=?new?HashMap<string,?atomicinteger="">();??
  • for?(int?i?=?0;?i?<?NUM_ITERATIONS;?i++)??
  • for?(String?a?:?sArr)?{??
  • AtomicInteger?value?=?atomicCounter.get(a);??
  • ???
  • if?(value?!=?null)?{??
  • value.incrementAndGet();??
  • }??
  • else?{??
  • atomicCounter.put(a,?new?AtomicInteger(1));??
  • }??
  • } ?

  • 入門級計數器 (不使用 containsKey, 使用 ?int[]):
    [java] view plaincopyprint?
  • HashMap<string,?int[]="">?intCounter?=?new?HashMap<string,?int[]="">();??
  • for?(int?i?=?0;?i?<?NUM_ITERATIONS;?i++)??
  • for?(String?a?:?sArr)?{??
  • int[]?valueWrapper?=?intCounter.get(a);??
  • ???
  • if?(valueWrapper?==?null)?{??
  • intCounter.put(a,?new?int[]?{?1?});??
  • }??
  • else?{??
  • valueWrapper[0]++;??
  • }??
  • } ?

  • Guava 語言的 MultiSet 可能更快一些.


    6. 結論
    優勝者是使用int數組的方式.


    轉載于:https://www.cnblogs.com/hthuang/p/4371515.html

    總結

    以上是生活随笔為你收集整理的Java高效计数器的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。