Collections.unmodifiableMap

1. Collections.unmodifiableMap 是什么?

Java的官方解释:

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K,? extends V> m)
Returns an unmodifiable view of the specified map. This method allows modules to provide users with "read-only" access to internal maps. Query operations on the returned map "read through" to the specified map, and attempts to modify the returned map, whether direct or via its collection views, result in an UnsupportedOperationException.

翻译过来就是:该方法返回了一个map的不可修改的视图umap, 为用户提供了一种生成只读容器的方法。如果尝试修改该容器umap, 将会抛出UnsupportedOperationException异常。

2. Collections.unmodifiableMap 能做什么?

在《重构-改善既有代码逻辑》一书中提到了封装集合的功能(Encapsulate Collection)。

我们在类中经常需要返回一个集合,比如mapA。如果直接返回成员变量mapA本身的话,相当于对外暴露了集合的引用,外部就可以随意修改该对象的集合,该对象可能对修改都一无所知,属性却发生了变化。

一种解决方法,就是将该集合修饰为private, 在返回集合的方法中采用Collections.unmodifiableMap(mapA),返回mapA的一个不可变的副本。且该方法要比我们自己去复制一个副本效率要高。

3. Collections.unmodifiableMap 构造的map真的不可修改吗?

遗憾的是该结论并不总是成立。对于map<key, value>中的内容value, unmodifiableMap仅仅保证的是它的引用不能被修改,如果value对应的是一个可变对象,那么该unmodifiableMap的内容还是可变的。见实例:

 

 1 public class UnmodifiableMap {
 2 
 3     public static void main(String[] args) {
 4 
 5         Map<String, Student> map = new HashMap<String, Student>();
 6         Student tom = new Student("tom", 3);
 7         map.put("tom", tom);
 8         map.put("jerry", new Student("jerry", 1));
 9 
10         Map<String, Student> unmodifiableMap = Collections.unmodifiableMap(map);
11         // unmodifiableMap.put("tom", new Student("tom", 11));  // tag1
12         tom.setAge(11); // tag2
13         System.out.println(unmodifiableMap);
14     }
15 
16 }
17 
18 // mutable
19 class Student {
20     private String name;
21     private int age;
22 
23     public Student(String name, int age) {
24         this.name = name;
25         this.age = age;
26     }
27 
28     public String getName() {
29         return name;
30     }
31 
32     public void setName(String name) {
33         this.name = name;
34     }
35 
36     public int getAge() {
37         return age;
38     }
39 
40     public void setAge(int age) {
41         this.age = age;
42     }
43 
44     @Override
45     public String toString() {
46         return "Student{" +
47                 "name='" + name + '\'' +
48                 ", age=" + age +
49                 '}';
50     }
51 }

 

代码中Student 对象是可变对象。在tag1处,尝试更换为另一个对象,引用发生了变化,会抛出UnsupportedOperationException异常。unmodifiableMap阻止了对其的修改

但如果引用不变,见tag2处,还是tom, 但对该对象内容作了修改,unmodifiableMap并未阻止该行为。unmodifiableMap的内容变为了

{jerry=Student{name='jerry', age=1}, tom=Student{name='tom', age=11}}

所以为了线程安全,在使用Collections.unmodifiableMap的同时,尽量让其中的内容实现为不可变对象。

 

 

posted @ 2017-01-05 20:00  smurf  阅读(2368)  评论(0编辑  收藏  举报