2013-02-14 133 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); 
} 

它更簡單,也更快。