Lambdas和putIfAbsent

时间:2013-02-14 14:25:09

标签: java lambda java-8

我发布了一个答案here,其中展示使用putIfAbsent ConcurrentMap方法的代码为:

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;
}

这种方法的主要缺点是你必须创建一个新的对象来放入地图,无论它是否会被使用。如果对象很重,这可能会产生很大的影响。

我想到这将是一个使用Lambdas的机会。我没有下载Java 8或者我能够直到它是正式的(公司政策)所以我不能测试这个但这样的东西是否有效且有效?

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

我希望使用lambda延迟new AtomicLong(0)的评估,直到确实应该创建它,因为它在地图中不存在。

正如您所看到的,这更加简洁和实用。

基本上我认为我的问题是:

  1. 这会有用吗?
  2. 或者我完全误解了lambdas?
  3. 有一天可能会有这样的工作吗?

4 个答案:

答案 0 :(得分:7)

更新2015-08-01

如下所述的computeIfAbsent方法确实是added to Java SE 8。语义似乎非常接近预发布版本。

此外,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月开始的。)ConcurrentHashMapV8 recent javadoc为{{3}}。

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

答案 1 :(得分:2)

不幸的是,它并不那么容易。您勾勒出的方法存在两个主要问题: 1.地图的类型需要从Map<String, AtomicLong>更改为Map<String, AtomicLongFunction>(其中AtomicLongFunction是一个函数接口,它具有不带参数的单个方法并返回{{1} })。 2.当您从地图中检索元素时,您需要每次都应用该函数以从中获取AtomicLong。这会导致每次检索时都创建一个新实例,这可能不是您想要的。

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

AtomicLong

你可以写

   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));

答案 2 :(得分: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) ) );
}

答案 3 :(得分:2)

请注意,使用Java 8 <!DOCTYPE html> <html <?php language_attributes(); ?>> <head> <meta charset="<?php bloginfo( 'charset' ); ?>" /> <meta name="viewport" content="width=device-width, initial scale=1" /> <title><?php wp_title( ' | ', true, 'right' ); ?></title> <link rel="stylesheet" type="text/css" href="<?php echo get_stylesheet_uri(); ?>" /> <?php wp_head(); ?> </head> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="<?php echo esc_url( home_url( '/' ) ); ?>">MoonLighting</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav navbar-right"> <li><a href="pages/adultanswers.php">Side Job</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">My Account <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Settings</a></li> <li><a href="#">Add Funds</a></li> <li role="separator" class="divider"></li> <li><a href="#">Sign Out</a></li> </ul> </li> </ul> </div> </div><!-- /.container-fluid --> </nav> <body <?php body_class(); ?>> <div id="wrapper" class="hfeed"> <header id="header" role="banner"> <section id="branding"> <!--I removed this for reasons unrelated--> </section> 完全没有必要拥有ConcurrentHashMap值。您可以安全地使用ConcurrentHashMap.merge

AtomicLong

它更简单,也更快。