public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
return new UnmodifiableMap<K,V>(m);
}
private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
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;
}
// ...
}
上面的代码截取自java.util.Collections,我看dubbo的代码中有用到unmodifiableMap方法,该方法返回的是一个被final修饰的Map,我想证明的是,这个不可修改的Map指的是Map中的对象地址不可修改,里面的对象若支持修改的话,其实也还是可以修改的。代码如下:
package com.itlong.whatsmars.base.collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Created by shenhongxi on 2016/6/14.
*/
public class UnmodifiableTest {
private Map<String, Point> startingLocations = new HashMap<String, Point>(3);
public UnmodifiableTest(){
startingLocations.put("LeftRook", new Point(1, 1));
startingLocations.put("LeftKnight", new Point(1, 2));
startingLocations.put("LeftCamel", new Point(1, 3));
//..more locations..
}
public Map<String, Point> getStartingLocations(){
return Collections.unmodifiableMap(startingLocations);
}
public static void main(String [] args){
UnmodifiableTest pieceLocations = new UnmodifiableTest();
Map<String, Point> locations = pieceLocations.getStartingLocations();
Point camelLoc = locations.get("LeftCamel");
System.out.println("The LeftCamel's start is at [ " + camelLoc.getX() + ", " + camelLoc.getY() + " ]");
//Try 1. update elicits Exception
try{
locations.put("LeftCamel", new Point(0,0));
} catch (java.lang.UnsupportedOperationException e){
System.out.println("Try 1 - Could not update the map!");
}
//Try 2. Now let's try changing the contents of the object from the unmodifiable map!
camelLoc.setLocation(0,0);
//Now see whether we were able to update the actual map
Point newCamelLoc = pieceLocations.getStartingLocations().get("LeftCamel");
System.out.println("Try 2 - Map updated! The LeftCamel's start is now at [ " + newCamelLoc.getX() + ", " + newCamelLoc.getY() + " ]"); }
}
class Point {
private float x;
private float y;
public Point(float x, float y){
setLocation(x, y);
}
public void setLocation(float x, float y){
this.x = x;
this.y = y;
}
public float getX(){
return x;
}
public float getY(){
return y;
}
}
结果如下:
The LeftCamel's start is at [ 1.0, 3.0 ] Try 1 - Could not update the map! Try 2 - Map updated! The LeftCamel's start is now at [ 0.0, 0.0 ]
附上两位大神的解释:
1.Cameron Skinner Collections.unmodifiableMap guarantees that the map will not be modified. It's mostly useful if you want to return a read-only view of an internal map from a method call, e.g: class A { private Map importantData; public Map getImportantData() { return Collections.unmodifiableMap(importantData); } } This gives you a fast method that does not risk the client changing your data. It's much faster and more memory efficient than returning a copy of the map. If the client really does want to modify the returned value then they can copy it themselves, but changes to the copy won't be reflected in A's data. If you are not returning map references to anyone else then don't bother making it unmodifiable unless you are paranoid about making it immutable. You can probably trust yourself to not change it. 2.Don Cameron Skinner's statement above that "Collections.unmodifiableMap guarantees that the map will not be modified" is actually only partly true in general, although it happens to be accurate for the specific example in the question (only because the Character object is immutable). I'll explain with an example. Collections.unmodifiableMap actually only gives you protection that the references to objects held in the map cannot be changed. It does that by restricting the 'put' into the map that it returns. However, the original encapsulated map can still be modified from outside the class because Collections.unmodifiableMap does not make any copies of the contents of the map. In the question posted by Paulo, the Character objects held in the map are luckily unmodifiable. However, in general this may not be true and the unmodifiability advertised by Collections.unmodifiableMap should not be the only safeguard.