2013-02-14 137 views
7

我张贴解答here,其中展示了使用ConcurrentMapputIfAbsent方法的代码如下:lambda表达式和的putIfAbsent

ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>(); 

public long addTo(String key, long value) { 
    // The final value it became. 
    long result = value; 
    // Make a new one to put in the map. 
    AtomicLong newValue = new AtomicLong(value); 
    // Insert my new one or get me the old one. 
    AtomicLong oldValue = map.putIfAbsent(key, newValue); 
    // Was it already there? Note the deliberate use of '!='. 
    if (oldValue != newValue) { 
    // Update it. 
    result = oldValue.addAndGet(value); 
    } 
    return result; 
} 

这种方法的主要缺点是,你必须创建一个新的对象装进映射它是否会被使用。如果物体很重,这可能会产生重大影响。

我突然想到,这将是使用Lambda表达式的机会。我没有下载Java 8 n'or我将能够,直到它是正式的(公司政策),所以我不能测试这个,但这样的事情是有效的和有效的?

public long addTo(String key, long value) { 
    return map.putIfAbsent(key,() -> new AtomicLong(0)).addAndGet(value); 
} 

我希望能使用拉姆达延迟new AtomicLong(0)的评价,直到它实际上是决定了它应建立,因为它没有在地图上存在。

正如你可以看到这是更简洁和实用。

从本质上讲,我想我的问题是:

  1. 将这项工作?
  2. 还是我完全曲解lambda表达式?
  3. 这样的工作有一天可能会奏效吗?
+2

你怎么无法下载Java 8并自行测试?您的公司是否阻止您在工作计算机上安装任何东西(甚至用于评估目的)?那么在你的个人身上尝试一下呢? – 2013-02-14 14:31:13

+0

@SimonLehmann - 顺便说一句 - 在您发表评论后,我安装了Java 8,它不仅没有Lambdas(还有一些额外的东西我应该安装,但没有解决),但DBVisualiser停止工作 - 所以我删除了它。抱歉。也许明年。 – OldCurmudgeon 2013-03-02 00:25:29

回答

6

UPDATE 2015年8月1日

如下所述确实已经added to Java SE 8computeIfAbsent方法。语义看起来非常接近预发布版本。

此外,computeIfAbsent以及一整套新的默认方法已被添加到Map界面。当然,地图通常不支持原子更新,但新方法为API增加了相当多的便利。


你试图做的是比较合理的,但遗憾的是它不与ConcurrentMap当前版本。然而,正在进行一项改进。新版本的并发库包含ConcurrentHashMapV8,其中包含一个新方法computeIfAbsent。这几乎可以让你做到你想要做的。使用这种新方法,如下所示的例子可以改写:

public long addTo(String key, long value) { 
    return map.computeIfAbsent(key,() -> new AtomicLong(0)).addAndGet(value); 
} 

有关ConcurrentHashMapV8的更多信息,请参阅并发利益邮件列表上Doug Lea的initial announcement thread。线下的几条消息是a followup message,它显示了一个非常类似于你想要做的例子。 (不过请注意旧的lambda语法,那条消息是从2011年8月开始的。)这里是recent javadocConcurrentHashMapV8

本工作旨在集成到Java 8中,但它还没有据我所知。此外,这仍然是一项工作,名称和规格可能会发生变化等。

+0

谢谢 - 除了它将被称为'ConcurrentHashMapV8'。多么可怕的想法! – OldCurmudgeon 2013-02-16 19:59:18

+1

我不确定当它真的集成到Java 8中时它是否仍然会被称为CHMV8。我怀疑Doug Lea称它为CHMV8,因此它可以与CHM同时用于应用程序,测试和基准测试目的。 Lea确实表示它打算替代CHM。 – 2013-02-16 20:23:05

+0

这是个好消息。感谢细节。 – OldCurmudgeon 2013-02-16 21:44:56

2

可惜这不是这么简单。您绘制的方法存在两个主要问题: 1.地图的类型需要从Map<String, AtomicLong>更改为Map<String, AtomicLongFunction>(其中AtomicLongFunction是一些函数接口,其中有一个方法不带参数并返回AtomicLong )。 2.当您检索从地图元素你需要每次都获得AtomicLong出来的应用功能。这会导致每次检索它时都会创建一个新实例,这可能不是您想要的。

具有按需运行的功能,以填补缺失值是一个很好的,但是,事实上,谷歌的番石榴库有一个地图,正是这么做的地图的想法;看到他们的MapMaker。事实上该代码将受益于Java的8 lambda表达式:不是

ConcurrentMap<Key, Graph> graphs = new MapMaker() 
     .concurrencyLevel(4) 
     .weakKeys() 
     .makeComputingMap(
      new Function<Key, Graph>() { 
      public Graph apply(Key key) { 
       return createExpensiveGraph(key); 
      } 
      }); 

你能写

ConcurrentMap<Key, Graph> graphs = new MapMaker() 
     .concurrencyLevel(4) 
     .weakKeys() 
     .makeComputingMap((Key key) -> createExpensiveGraph(key)); 

ConcurrentMap<Key, Graph> graphs = new MapMaker() 
     .concurrencyLevel(4) 
     .weakKeys() 
     .makeComputingMap(this::createExpensiveGraph); 
+0

不知何故,我只是知道我的解决方案要简单得多,所以我会有效地将** lambda **添加到地图,而不是lambda的**结果** ......哦。 – OldCurmudgeon 2013-02-14 15:46:56

2

AtomicLong是不是一个真正的重物。对于较重的对象,我会考虑一个懒惰的代理,并提供一个lambda到那个创建对象,如果需要的话。

class MyObject{ 
    void doSomething(){} 
} 

class MyLazyObject extends MyObject{ 
    Funktion create; 
    MyLazyObject(Funktion create){ 
     this.create = create; 
    } 
    MyObject instance; 
    MyObject getInstance(){ 
     if(instance == null) 
      instance = create.apply(); 
     return instance; 
    } 
    @Override void doSomething(){getInstance().doSomething();} 
} 

public long addTo(String key, long value) { 
    return map.putIfAbsent(key, new MyLazyObject(() -> new MyObject(0))); 
} 
+0

我用一个'AtomicLong'作为示例的地方持有人,我的确在考虑更重的对象。这看起来很有效,但是还有很多东西可以用来实现在lambda中应该是天生的懒惰。 – OldCurmudgeon 2013-02-14 15:50:37

+0

使用番石榴库似乎更容易。但是编写懒惰的对象使得你可以独立于Map实现。 您也可以创建一个委托所有方法调用的通用InvocationHandler,请参阅[Proxy](http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html)。但是你是对的,在大多数情况下这将是一个真正的矫枉过正。 – 2013-02-14 18:16:19

1

请注意,使用Java 8 ConcurrentHashMap完全没有必要拥有AtomicLong值。你可以安全地使用ConcurrentHashMap.merge

ConcurrentMap<String, Long> map = new ConcurrentHashMap<String, Long>(); 

public long addTo(String key, long value) { 
    return map.merge(key, value, Long::sum); 
} 

它更简单,也更快。