我有以下类,其中声明了一个私有线程安全集合:
final private ConcurrentHashMap<Book,BookLog> booklogMap;
根据这个page,因为日期是可变的并且它在不可变类中使用,所以必须制作防御性副本以避免在创建后更改对象。他们在构造函数中制作一个防御性副本,如下所示:
fDateOfDiscovery = new Date(aDateOfDiscovery.getTime())
在这样的吸气器中:
public Date getDateOfDiscovery() {
return new Date(fDateOfDiscovery.getTime());
}
如何在构造函数中正确创建ConcurrentHashMap的防御副本?我不能使用Collections.modifiableMap()
,因为它会遇到强制转换问题。
答案 0 :(得分:1)
您可能希望也可能不希望使用Collections.unmodifiableMap()
,因为它会返回Map
,而不是ConcurrentHashMap
,而您可能无法投放它(尽管我没有&#39} ;尝试过)。这可能不是一件坏事,因为其他类可能不需要知道这个特殊的Map
属于Concurrent Hash变种。
@resueman在评论中建议你可以使用
new ConcurrentHashMap<Book, Booklog>(booklogMap);
这会创建ConcurrentHashMap
本身的(防御性)副本,但不会创建Map
内容的副本。这可能没问题,也可能没有,这取决于Map
的用户想要用它做什么。
并且,如果您决定需要复制内容,则必须决定是否复制这些副本包含的字段值。等等,直到你停下来。
答案 1 :(得分: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"));
}
}
原创用于历史目的: 一些事情,
首先,如果将变量/ fields / constants全部定义为接口而不是特定的具体类,则更好。因此,您应该将地图定义为:
//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所有Book(s)和BookLog(s)。
答案 2 :(得分:0)
你在评论中已经说过Book和BookLog是不可变的,所以你不需要制作它们的防御性副本。但你也在评论中说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}
}
没有复制,这没有任何意义,因为它确实:
{{1}}
如果这些答案对您没有帮助,那么您真的需要澄清您的问题。哪些对象是不可变的?你试图阻止什么突变?你能举个例子吗?