1. 程式人生 > >Java8使用Map中的computeIfAbsent方法構建本地快取

Java8使用Map中的computeIfAbsent方法構建本地快取

一、概念及使用介紹

在JAVA8的Map介面中,增加了一個方法computeIfAbsent,此方法簽名如下:

  1. public V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 

Map介面的實現類如HashMap,ConcurrentHashMap,HashTable等繼承了此方法,通過此方法可以構建JAVA本地快取,降低程式的計算量,程式的複雜度,使程式碼簡潔,易懂。

此方法首先判斷快取MAP中是否存在指定key的值,如果不存在,會自動呼叫mappingFunction(key)計算key的value,然後將key = value放入到快取Map,java8會使用thread-safe的方式從cache中存取記錄。

如果mappingFunction(key)返回的值為null或丟擲異常,則不會有記錄存入map

二、程式碼樣例

  1. import java.util.HashMap;  
  2. import java.util.HashSet;  
  3. import java.util.Map;  
  4. import java.util.concurrent.ConcurrentHashMap;  
  5. import java.util.concurrent.ExecutorService;  
  6. import java.util.concurrent.Executors;  
  7. import java.util.concurrent.TimeUnit;  
  8. publicclass Main {  
  9. static Map<Integer, Integer> cache = new ConcurrentHashMap<>();  
  10. publicstaticvoid main(String[] args) throws InterruptedException {  
  11.         cache.put(00);  
  12.         cache.put(11);  
  13. // 普通方式
  14.         System.out.println("Fibonacci(7) = " + fibonacci(7));  
  15. // 採用java7的同步執行緒方式及java8的本地快取的方式
  16.         System.out.println("FibonacciJava8(7) = " + fibonacciJava8(7));  
  17.         System.out.println("FibonacciJava7(7) = " + fibonacciJava7(7));  
  18. // 構建多值Map樣例程式碼
  19.         Map<String, HashSet<String>> map1 = new HashMap<>();  
  20.         map1.computeIfAbsent("fruits", k -> genValue(k)).add("apple");  
  21.         map1.computeIfAbsent("fruits", k -> genValue(k)).add("orange");  
  22.         map1.computeIfAbsent("fruits", k -> genValue(k)).add("pear");  
  23.         map1.computeIfAbsent("fruits", k -> genValue(k)).add("banana");  
  24.         map1.computeIfAbsent("fruits", k -> genValue(k)).add("water");  
  25.         System.out.println(map1);  
  26. //測試多執行緒併發處理,是否同步操作
  27.         Map<String, String> map2 = new ConcurrentHashMap<>();  
  28.         ExecutorService exec = Executors.newCachedThreadPool();  
  29. for (int i = 0; i < 5; i++) {  
  30.             exec.execute(() -> {  
  31.                 map2.computeIfAbsent("name", k -> genValue2(k));  
  32.                 map2.computeIfAbsent("addr", k -> genValue2(k));  
  33.                 map2.computeIfAbsent("email", k -> genValue2(k));  
  34.                 map2.computeIfAbsent("mobile", k -> genValue2(k));  
  35.             });  
  36.         }  
  37.         exec.shutdown();  
  38.         exec.awaitTermination(1, TimeUnit.SECONDS);  
  39.         System.out.println(map2);  
  40.     }  
  41. static HashSet<String> genValue(String str) {  
  42. returnnew HashSet<String>();  
  43.     }  
  44. static String genValue2(String str) {  
  45.         System.out.println("===");  
  46. return str + "2";  
  47.     }  
  48. /**  
  49.      * 普通的實現方式 普通方式使用大量的計算,存在效能問題. 並且計算量隨著n的增加呈指數級增加,需要用到一些快取策略,並且是執行緒安全的.  
  50.      *   
  51.      * @param n  
  52.      * @return  
  53.      */
  54. staticint fibonacci(int n) {  
  55. if (n == 0 || n == 1)  
  56. return n;  
  57.         System.out.println("calculating Fibonacci(" + n + ")");  
  58. return fibonacci(n - 2) + fibonacci(n - 1);  
  59.     }  
  60. /**  
  61.      * 採用java8的本地快取方式 如果快取MAP中不存在指定key的值,會自動呼叫mappingFunction(key)計算key的value  
  62.      * 然後將key = value放入到快取Map,java8會使用thread-safe的方式從cache中存取記錄  
  63.      *   
  64.      * @param n  
  65.      * @return  
  66.      */
  67. staticint fibonacciJava8(int n) {  
  68. return cache.computeIfAbsent(n, (key) -> {  
  69.             System.out.println("calculating FibonacciJava8 " + n);  
  70. return fibonacciJava8(n - 2) + fibonacciJava8(n - 1);  
  71.         });  
  72.     }  
  73. /**  
  74.      * 在java7中的實現方式  
  75.      * 在java7中,通過synchronized進行執行緒同步,檢查快取是否存在key對應的值,如果不存在才進行計算並放入快取中  
  76.      * 為了更好的效能,需要使用 double-checked locking,那樣程式碼會更復雜  
  77.      *   
  78.      * @param n  
  79.      * @return  
  80.      */
  81. staticint fibonacciJava7(int n) {  
  82. if (n == 0 || n == 1)  
  83. return n;  
  84.         Integer result = cache.get(n);  
  85. if (result == null) {  
  86. synchronized (cache) {  
  87.                 result = cache.get(n);  
  88. if (result == null) {  
  89.                     System.out.println("calculating FibonacciJava7(" + n + ")");  
  90.                     result = fibonacciJava7(n - 2) + fibonacciJava7(n - 1);  
  91.                     cache.put(n, result);  
  92.                 }  
  93.             }  
  94.         }  
  95. return result;  
  96.     }  
  97. }  

三、程式執行結果

  1. calculating Fibonacci(7)  
  2. calculating Fibonacci(5)  
  3. calculating Fibonacci(3)  
  4. calculating Fibonacci(2)  
  5. calculating Fibonacci(4)  
  6. calculating Fibonacci(2)  
  7. calculating Fibonacci(3)  
  8. calculating Fibonacci(2)  
  9. calculating Fibonacci(6)  
  10. calculating Fibonacci(4)  
  11. calculating Fibonacci(2)  
  12. calculating Fibonacci(3)  
  13. calculating Fibonacci(2)  
  14. calculating Fibonacci(5)  
  15. calculating Fibonacci(3)  
  16. calculating Fibonacci(2)  
  17. calculating Fibonacci(4)  
  18. calculating Fibonacci(2)  
  19. calculating Fibonacci(3)  
  20. calculating Fibonacci(2)  
  21. Fibonacci(7) = 13
  22. calculating FibonacciJava8 7
  23. calculating FibonacciJava8 5
  24. calculating FibonacciJava8 3
  25. calculating FibonacciJava8 2
  26. calculating FibonacciJava8 4
  27. calculating FibonacciJava8 6
  28. FibonacciJava8(7) = 13
  29. FibonacciJava7(7) = 13
  30. {fruits=[orange, banana, apple, pear, water]}  
  31. ===  
  32. ===  
  33. ===  
  34. ===  
  35. {name=name2, mobile=mobile2, addr=addr2, email=email2}