下面的类有一个私有的线程安全集合,声明如下:
final private ConcurrentHashMap<Book,BookLog> booklogMap;根据这个page,因为日期是可变的,并且它是在不可变的类中使用的,所以必须创建一个防御性的副本,以避免在创建后更改对象。它们在构造函数中创建一个防御性副本,如下所示:
fDateOfDiscovery = new Date(aDateOfDiscovery.getTime()) 在像这样的getter中:
public Date getDateOfDiscovery() {
return new Date(fDateOfDiscovery.getTime());
}那么,如何在构造函数中正确地创建ConcurrentHashMap的防御性副本呢?我不能使用Collections.modifiableMap(),因为它会遇到cast问题。
发布于 2016-02-12 05:20:02
您可能想要也可能不想使用Collections.unmodifiableMap(),因为它返回一个Map,而不是一个ConcurrentHashMap,而且您可能不能强制转换它(尽管我还没有尝试过)。这可能不是一件坏事,因为其他类可能不需要知道这个特定的Map是并发散列的变体。
@resueman在评论中建议你可以使用
new ConcurrentHashMap<Book, Booklog>(booklogMap);这会创建ConcurrentHashMap本身的(防御性)副本,但不会创建Map内容的副本。这可能是可以的,也可能不是,这取决于Map的用户想要用它做什么。
而且,如果您决定需要复制内容,则必须决定是否复制这些副本包含的字段值。以此类推,直到你停下来。
发布于 2016-02-12 06:15:54
使用示例编辑
这将复制输入映射,在请求时安全地提供映射的不可变副本,并在需要时安全地返回映射的元素:
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"));
}
}最初用于历史目的:几件事,
首先,如果您将变量/字段/常量都定义为接口而不是特定的具体类,这会更好。因此,您应该将您的映射定义为:
//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);
}
}第三,为了拥有真正的不变性,你需要让对象的整个层次结构都是不可变的。所以你的类Book和BookLog也需要是不可变的。
否则,您需要在构造函数中逐个deep copy所有的书和BookLog。
发布于 2016-02-12 09:40:10
你在评论中说过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}
}如果这些答案对你没有帮助,那么你真的需要澄清你的问题。你的哪些对象是不可变的?您想要防止什么突变?你能举个例子吗?
https://stackoverflow.com/questions/35350075
复制相似问题