文档章节

java8 使用Map中的computeIfAbsent方法构建本地缓存,提高程序效率

cloud-coder
 cloud-coder
发布于 2014/04/04 16:01
字数 879
阅读 4220
收藏 32

一、概念及使用介绍 

     在JAVA8的Map接口中,增加了一个方法computeIfAbsent,此方法签名如下:

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

二 代码样例

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main {
	static Map<Integer, Integer> cache = new ConcurrentHashMap<>();

	public static void main(String[] args) throws InterruptedException {
		cache.put(0, 0);
		cache.put(1, 1);
		// 普通方式
		System.out.println("Fibonacci(7) = " + fibonacci(7));
		// 采用java7的同步线程方式及java8的本地缓存的方式
		System.out.println("FibonacciJava8(7) = " + fibonacciJava8(7));
		System.out.println("FibonacciJava7(7) = " + fibonacciJava7(7));

		// 构建多值Map样例代码
		Map<String, HashSet<String>> map1 = new HashMap<>();
		map1.computeIfAbsent("fruits", k -> genValue(k)).add("apple");
		map1.computeIfAbsent("fruits", k -> genValue(k)).add("orange");
		map1.computeIfAbsent("fruits", k -> genValue(k)).add("pear");
		map1.computeIfAbsent("fruits", k -> genValue(k)).add("banana");
		map1.computeIfAbsent("fruits", k -> genValue(k)).add("water");
		System.out.println(map1);

		//测试多线程并发处理,是否同步操作
		Map<String, String> map2 = new ConcurrentHashMap<>();
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 5; i++) {
			exec.execute(() -> {
				map2.computeIfAbsent("name", k -> genValue2(k));
				map2.computeIfAbsent("addr", k -> genValue2(k));
				map2.computeIfAbsent("email", k -> genValue2(k));
				map2.computeIfAbsent("mobile", k -> genValue2(k));
			});
		}
		exec.shutdown();
		exec.awaitTermination(1, TimeUnit.SECONDS);
		System.out.println(map2);
	}

	static HashSet<String> genValue(String str) {
		return new HashSet<String>();
	}

	static String genValue2(String str) {
		System.out.println("===");
		return str + "2";
	}

	/**
	 * 普通的实现方式 普通方式使用大量的计算,存在性能问题. 并且计算量随着n的增加呈指数级增加,需要用到一些缓存策略,并且是线程安全的.
	 * 
	 * @param n
	 * @return
	 */
	static int fibonacci(int n) {
		if (n == 0 || n == 1)
			return n;

		System.out.println("calculating Fibonacci(" + n + ")");
		return fibonacci(n - 2) + fibonacci(n - 1);
	}

	/**
	 * 采用java8的本地缓存方式 如果缓存MAP中不存在指定key的值,会自动调用mappingFunction(key)计算key的value
	 * 然后将key = value放入到缓存Map,java8会使用thread-safe的方式从cache中存取记录
	 * 
	 * @param n
	 * @return
	 */
	static int fibonacciJava8(int n) {
		return cache.computeIfAbsent(n, (key) -> {
			System.out.println("calculating FibonacciJava8 " + n);
			return fibonacciJava8(n - 2) + fibonacciJava8(n - 1);
		});
	}

	/**
	 * 在java7中的实现方式
	 * 在java7中,通过synchronized进行线程同步,检查缓存是否存在key对应的值,如果不存在才进行计算并放入缓存中
	 * 为了更好的性能,需要使用 double-checked locking,那样代码会更复杂
	 * 
	 * @param n
	 * @return
	 */
	static int fibonacciJava7(int n) {
		if (n == 0 || n == 1)
			return n;

		Integer result = cache.get(n);

		if (result == null) {
			synchronized (cache) {
				result = cache.get(n);

				if (result == null) {
					System.out.println("calculating FibonacciJava7(" + n + ")");
					result = fibonacciJava7(n - 2) + fibonacciJava7(n - 1);
					cache.put(n, result);
				}
			}
		}
		return result;
	}
}

三,程序运行结果

calculating Fibonacci(7)
calculating Fibonacci(5)
calculating Fibonacci(3)
calculating Fibonacci(2)
calculating Fibonacci(4)
calculating Fibonacci(2)
calculating Fibonacci(3)
calculating Fibonacci(2)
calculating Fibonacci(6)
calculating Fibonacci(4)
calculating Fibonacci(2)
calculating Fibonacci(3)
calculating Fibonacci(2)
calculating Fibonacci(5)
calculating Fibonacci(3)
calculating Fibonacci(2)
calculating Fibonacci(4)
calculating Fibonacci(2)
calculating Fibonacci(3)
calculating Fibonacci(2)
Fibonacci(7) = 13
calculating FibonacciJava8 7
calculating FibonacciJava8 5
calculating FibonacciJava8 3
calculating FibonacciJava8 2
calculating FibonacciJava8 4
calculating FibonacciJava8 6
FibonacciJava8(7) = 13
FibonacciJava7(7) = 13
{fruits=[orange, banana, apple, pear, water]}
===
===
===
===
{name=name2, mobile=mobile2, addr=addr2, email=email2}

四、参考

http://www.java8.org/caching-with-ConcurrentHashMap-in-java-8.html

JDK8 API

http://stackoverflow.com/questions/19278443/how-do-i-use-the-new-computeifabsent-function

© 著作权归作者所有

共有 人打赏支持
cloud-coder
粉丝 248
博文 191
码字总数 135000
作品 0
广州
架构师
私信 提问
加载中

评论(1)

梦里奇迹
这里的单线程执行的,fibonacciJava7根本就不需要asynchronized,也不需要写那么麻烦。可以计一下时,对于这里的单线程来说,fibonacciJava8虽然写法简单,但是效率很低。
java8的重要特性及其他,昨天答应的发java8╭(╯ε╰)╮

【注意】本文节选自是 DZone 指南 Java 生态系统的专题文章,作者Trisha Gee是Java资深工程师和布道者。在本文中,Trisha Gee阐述了Java 8的重要特性以及使用的原因,由OneAPM工程师翻译。 ...

默默学习中
2016/03/28
1K
9
为什么选择 Java 8 ?

本文是 DZone 指南 Java 生态系统的专题文章。点击链接可阅读更多见解深刻的文章、行业统计信息,系 OneAPM工程师编译整理。 在很多情况下,Java8 都能提升应用性能,而无需任何改变或性能调...

OneAPM蓝海讯通
2015/11/04
117
0
LinkedHashMap源码分析-java8

得益于昨天网易的面试,所以重新认识了一个集合,回来后赶紧做了分析,继续努力~ps:面试官真的很nice,希望好运~ 1.特性分析 说明:因为LinkedHashMap单词太长,所以以下都用LHM替代 基本...

caoxiaohong1005
04/12
0
0
聊聊redisson的RMap的computeIfAbsent操作

序 本文主要研究一下redisson的RMap的computeIfAbsent操作 实例 源码分析 ConcurrentMap.computeIfAbsent java/util/concurrent/ConcurrentMap.java computeIfAbsent当该key不存在时,返回的......

go4it
09/25
0
0
Java8简明指南

Java8简明指南 欢迎来到Java8简明指南。本教程将一步一步指导你通过所有新语言特性。由短而简单的代码示例,带你了解如何使用默认接口方法,lambda表达式,方法引用和可重复注解。本文的最后你会...

王爵nice
2015/09/15
364
2

没有更多内容

加载失败,请刷新页面

加载更多

重磅!亚洲诚信实力斩获:“2018 DigiCert/Symantec 年度最佳创新合作伙伴”大奖

2018年11月13日-16日,全球顶级数字证书厂商,DigiCert/Symantec亚太区圆桌会议(Asia Partner Roundtable 2018)在日本大阪隆重召开。 亚洲诚信作为DigiCert/Symantec亚太区白金战略合作伙伴和...

亚洲诚信
28分钟前
2
0
始于阿里,回归社区:阿里8个项目进入CNCF云原生全景图

摘要: 一群技术理想主义者,与太平洋另一边的技术高手们正面PK,在这场躲不开的战役中,一起认真一把。 破土而出的生命力,源自理想主义者心底对技术的信念。 云原生技术正席卷全球,云原生...

阿里云官方博客
35分钟前
3
0
修改this指向(bind、call 和 apply)

一、bind bind 的其中一个用法就是:绑定函数,使其无论怎么样调用都用相同的 this 示例: var obj = { getThis: function() { console.log(this); }};obj.getThis()...

文文1
今天
1
0
WSL安装JDK8

下载地址 JDK_URL https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html UNLIMITED_STRENGTH_URL https://www.oracle.com/technetwork/java/javase/down......

terwergreen
今天
4
0
sparkStreaming基本概念

概述 Spark Streaming 是 Spark Core API 的扩展, 它支持弹性的, 高吞吐的, 容错的实时数据流的处理. 数据可以通过多种数据源获取, 例如 Kafka, Flume, Kinesis 以及 TCP sockets, 也可以通过...

freeli
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部