2016-02-11 51 views
1

我有一個聲明爲這樣的私人線程安全的收集提供了以下類防禦性複製:是怎樣煉成的線程安全集合

final private ConcurrentHashMap<Book,BookLog> booklogMap; 

根據這一page,因爲日期是可變的,它是在不可變類中使用,必須創建防禦副本以避免在創建後更改對象。他們讓在構造一個防守副本是這樣的:

fDateOfDiscovery = new Date(aDateOfDiscovery.getTime()) 

,並在吸氣劑是這樣的:

public Date getDateOfDiscovery() { 
    return new Date(fDateOfDiscovery.getTime()); 
    } 

那麼如何將我正常在我的構造函數創建的ConcurrentHashMap的防守副本?我不能使用Collections.modifiableMap(),因爲它會遇到投射問題。

+0

'new ConcurrentHashMap (booklogMap)'?你是否需要製作內容的副本? – resueman

+0

爲什麼要使用'Collections.unmodifiableMap()'遇到一個投射問題? – Tunaki

+0

@Tunaki - 當我厭倦它時,它說要向Collections.unmodifiableMap()添加一個ConcurrentHashMap的強制轉換。 – AnnabelleRosemond

回答

1

您可能會也可能不希望使用Collections.unmodifiableMap(),因爲這會返回Map而不是ConcurrentHashMap,並且您可能無法投射它(儘管我沒有嘗試過)。這可能不是什麼壞事,因爲其他類可能不需要知道這個特定的Map是並行哈希變體。

@resueman建議中,你可以使用

new ConcurrentHashMap<Book, Booklog>(booklogMap); 

創建該ConcurrentHashMap本身的(防守)的副本,但不創建的Map的內容拷貝意見。這可能是好的,或者它可能不會,這取決於Map的用戶想要用它做什麼。

而且,如果您決定需要複製內容,則必須決定是否複製這些副本包含的字段值。等等,直到你停下來。

+0

我的問題是,我一直在使用你的方法,並且reueman建議。但是,它不會複製。用戶將能夠添加一本書以及可能隨附的任何日誌,因此它會不時發生變化。我將如何複製一切(字段值)? – AnnabelleRosemond

0

與例如編輯

這將拷貝輸入地圖,安全地提供地圖時請求的不可變拷貝,並安全地在需要時返回的地圖的一個元素:

import java.util.Collections; 
import java.util.Map; 
import java.util.concurrent.ConcurrentHashMap; 


public class CopyMap { 

    //just an example Long and String are both immutable 
    //replace Long for Book (Book has to implement hashcode and equals. 
    // replace String for Booklog 
    private final Map<Long, String> map; 

    public CopyMap(Map<Long,String> map) { 
     this.map = new ConcurrentHashMap<>(map); 
    } 

    // get a non editable copy of the whole map 
    // threadsafe because this.map is concurrentHashMap 
    // no synchronization needed 
    public Map<Long,String> getMap() { 
     return Collections.unmodifiableMap(this.map); 
    } 

    //get only one of the elements 
    //thread safe because implementation is ConcurrentHashMap 
    //no synchronization needed 
    public String get(Long key) { 
     return map.get(key); 
    } 

    //just to try it, with -ea enabled 
    public static void main(String... args) { 
     final Map<Long,String> map1 = new ConcurrentHashMap<>(); 
     map1.put(1l,"1"); 
     map1.put(2l,"2"); 
     map1.put(3l,"3"); 

     final CopyMap copyMap = new CopyMap(map1); 

     assert(copyMap.getMap().equals(map1)); 
     assert(copyMap.getMap()!=map1); 

     assert(copyMap.get(1l).equals("1")); 
     assert(copyMap.get(2l).equals("2")); 
     assert(copyMap.get(3l).equals("3")); 
    } 
} 

原來歷史的目的: 有幾件事情,

首先,它是更好,如果你定義了你的變量/場/ constan全部作爲接口而不是具體的具體類。所以,你應該定義你映射爲:

//conventions usually prefer private final, as opposed to final private 
private final Map<Book,BookLog> booklogMap; 

然後,其次,你的確可以使用Collections.unmodifiableMap()

因此,一個例子是:

//package and imports here 
public class MyClass { 

    private final Map<Book,BookLog> booklogMap; 

    public MyClass(Map<Book, BookLog> booklogMap) { 
    this.booklogMap = Collections.unmodifiableMap(booklogMap); 
    } 
} 

第三,你有真正的不變性,你需要使整個對象的層次不變。所以你的類BookBookLog也需要是不可變的。

否則,您需要在構造函數中一一列出所有Book和s BookLog。

+0

感謝您的閱讀,但是有幾個問題,我使用了一個concurrenthashmap,而Book/BookLog實際上是不可變的 – AnnabelleRosemond

+0

請參閱我的編輯:我創建了一個小例子。我希望它有幫助。它使用Long和String,因爲這2個是不可變的(用你的類替換它們)。 – portenez

+0

順便提一下,防禦副本通常只在getters/setters中需要,以便客戶端不會更改您的對象擁有的集合。然而,如果這就是你想要的(保護擁有的集合免受外部突變)'''Collections.unmodifiable()'''將以同樣的方式保護你的集合。 – portenez

0

你在評論中說過Book和BookLog是不可變的,所以你不需要製作它們的防禦副本。但你也說,在評論認爲new ConcurrentHashMap<Book, Booklog>(booklogMap);不進行復印,它沒有任何意義,因爲它的作用:

public static void main(String[] args) { 
    Map<String, Integer> map = new ConcurrentHashMap<>(); 
    map.put("apples", 3); 
    System.out.println(map); 
    // prints {apples=3} 
    Map<String, Integer> map2 = new ConcurrentHashMap<>(map); 
    map2.put("oranges", 1); 
    System.out.println(map2); 
    // prints {oranges=1, apples=3} 
    System.out.println(map); 
    //prints {apples=3} 
} 

如果這些問題的答案沒有幫助你,那麼你真的需要澄清你的問題。你的哪個對象是不可變的?你試圖阻止什麼樣的突變?你能給個例子嗎?

+0

在另一篇文章中,幾個答案表明它不會創建副本。對於那個很抱歉。我試圖阻止的突變是用戶在構造函數中創建bookLogMap後添加到集合中。具體如何用戶可以修改我鏈接的頁面中的日期。我不想那樣。 – AnnabelleRosemond

+0

@AnnabelleRosemond,你的意思是你有一個'getBookMap'方法來公開地圖嗎?在這種情況下,你可以刪除這個方法,或者在你現在可以工作的情況下在getter中製作一個防禦副本。 – DavidS

相關問題