使用 Collections.unmodifiableMap() 方法,实现返回不可修改的集合。

在实际项目开发中,有时候需要方法返回不可修改的集合或对象,比如:Map。

如以下例子:

1
2
3
4
5
6
7
8
9
10
11
12
/** 缓存的 Map */
private static Map<String, String> cachedMap = new HashMap<>();

/** 刷新缓存 */
private static void reloadCache() {
// ...
}

/** 获取缓存的 Map */
public static Map<String, String> getCachedMap() {
return cachedMap;
}

cachedMap作为私有对象,使得只有通过 getCachedMap() 方法获取缓存的 Map。

但是由于返回的是引用,其它调用到的地方获取到返回的 Map 后还是可以对其进行修改,这不符合闭合原则。我们希望返回的是副本或通过某种办法让其不能修改。

在 java.util 包中的 Collections 类有个 unmodifiableMap 方法,可以简单的解决上述问题,查看其源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
return new UnmodifiableMap<>(m);
}

private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
private static final long serialVersionUID = -1034234728574286014L;

private final Map<? extends K, ? extends V> m;

UnmodifiableMap(Map<? extends K, ? extends V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
}

public int size() {return m.size();}
public boolean isEmpty() {return m.isEmpty();}
public boolean containsKey(Object key) {return m.containsKey(key);}
public boolean containsValue(Object val) {return m.containsValue(val);}
public V get(Object key) {return m.get(key);}

public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public V remove(Object key) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}

// ...
}

可以发现,unmodifiableMap 方法返回的是一个 UnmodifiableMap 包装类。这个类里面保存了一个 Map 成员属性 m。同时这个类也实现了 Map 接口,说明它具有 Map 的所有特性。

  • 当调用其读相关方法(size、isEmpty、get)时,间接调用的是成员属性 m 的对应方法。
  • 当调用其修改相关方法(put、remove、clear)时,直接抛出 UnsupportedOperationException 异常,不让修改。

所以,Collections.unmodifiableMap()方法通过返回一个 UnmodifiableMap 包装类实现返回的 Map 不可修改,其原理简单、调用方便。

但还需要注意的是:如果 Map 的 value 是对象类型的话,如:Map<String, User>,虽然 User 本身引用不能修改,但是可以通过拿到 User 引用修改 User 内部数据。

同理:Collections 除了 unmodifiableMap,还有 unmodifiableCollection、unmodifiableList、unmodifiableSet 等方法,其原理是类似的: