I have a map of constants, like this:
我有一个常量图,如下所示:
private static Map<String, Character> _typesMap =
new HashMap<String, Character>() {
{
put ("string", 'S');
put ("normalizedString", 'N');
put ("token", 'T');
// (...)
}
Do I really need to use Collections.unmodifiableMap()
to create this map? What is the advantage of using it? Are there any disadvantages of not using it, besides the obvious fact that they are not really becoming constant?
我真的需要使用Collections.unmodifiableMap()来创建这个地图吗?使用它有什么好处?有没有使用它的缺点,除了明显的事实,它们并没有真正变得不变?
3 个解决方案
#1
62
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:
Collections.unmodifiableMap保证不会修改地图。如果要从方法调用返回内部映射的只读视图,则最有用,例如:
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.
这为您提供了一种快速方法,可以避免客户端更改数据的风险。它比返回地图副本更快,内存效率更高。如果客户端确实想要修改返回的值,那么他们可以自己复制它,但是对副本的更改不会反映在A的数据中。
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
26
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.
Cameron Skinner上面的声明“Collections.unmodifiableMap保证地图不会被修改”实际上通常只是部分正确,尽管它恰好对于问题中的特定示例是准确的(仅因为Character对象是不可变的)。我将用一个例子来解释。
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.
Collections.unmodifiableMap实际上只为您提供保护,即无法更改对地图中保存的对象的引用。它通过将'put'限制在它返回的地图中来实现。但是,仍然可以从类外部修改原始封装映射,因为Collections.unmodifiableMap不会生成映射内容的任何副本。
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. For instance, see the example below.
在Paulo发布的问题中,地图中保存的角色对象幸运地无法修改。但是,通常情况可能并非如此,Collections.unmodifiableMap所宣传的不可修改性不应该是唯一的保护措施。例如,请参阅下面的示例。
import java.awt.Point;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class SeeminglyUnmodifiable {
private Map<String, Point> startingLocations = new HashMap<>(3);
public SeeminglyUnmodifiable(){
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){
SeeminglyUnmodifiable pieceLocations = new SeeminglyUnmodifiable();
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() + " ]"); }
}
When you run this example, you see:
运行此示例时,您会看到:
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 ]
The startingLocations map is encapsulated and only returned by leveraging Collections.unmodifiableMap in the getStartingLocations method. However, the scheme is subverted by getting access to any object and then changing it, as seen in "Try 2" in the code above. Suffice to say that one can only rely on Collections.unmodifiableMap to give a truly unmodifiable map IF the objects held in the map are themselves immutable. If they're not, we'd want to either copy the objects in the map or otherwise restrict access to the object's modifier methods, if possible.
startsLocations映射是封装的,只能通过在getStartingLocations方法中利用Collections.unmodifiableMap来返回。但是,通过访问任何对象然后更改它来破坏该方案,如上面代码中的“尝试2”所示。可以说,如果地图中保存的对象本身是不可变的,那么只能依靠Collections.unmodifiableMap来提供真正不可修改的地图。如果不是,我们要么想要复制地图中的对象,要么限制访问对象的修改方法,如果可能的话。
#3
-2
Wrapping the Map is to ensure the caller won't change the collection. While this is useful in testing, you really should find this sort of bug there, it may not be so useful in production. A simple workaround is to have your own wrapper like.
包装地图是为了确保调用者不会更改集合。虽然这在测试中很有用,但你真的应该在那里找到这种bug,它在生产中可能不那么有用。一个简单的解决方法是拥有自己的包装器。
public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> map) {
assert (map = Collections.unmodifiableMap(map)) != null;
return map;
}
This only wraps the map when assertions are turned on.
这仅在断言打开时包装地图。
#1
62
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:
Collections.unmodifiableMap保证不会修改地图。如果要从方法调用返回内部映射的只读视图,则最有用,例如:
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.
这为您提供了一种快速方法,可以避免客户端更改数据的风险。它比返回地图副本更快,内存效率更高。如果客户端确实想要修改返回的值,那么他们可以自己复制它,但是对副本的更改不会反映在A的数据中。
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
26
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.
Cameron Skinner上面的声明“Collections.unmodifiableMap保证地图不会被修改”实际上通常只是部分正确,尽管它恰好对于问题中的特定示例是准确的(仅因为Character对象是不可变的)。我将用一个例子来解释。
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.
Collections.unmodifiableMap实际上只为您提供保护,即无法更改对地图中保存的对象的引用。它通过将'put'限制在它返回的地图中来实现。但是,仍然可以从类外部修改原始封装映射,因为Collections.unmodifiableMap不会生成映射内容的任何副本。
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. For instance, see the example below.
在Paulo发布的问题中,地图中保存的角色对象幸运地无法修改。但是,通常情况可能并非如此,Collections.unmodifiableMap所宣传的不可修改性不应该是唯一的保护措施。例如,请参阅下面的示例。
import java.awt.Point;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class SeeminglyUnmodifiable {
private Map<String, Point> startingLocations = new HashMap<>(3);
public SeeminglyUnmodifiable(){
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){
SeeminglyUnmodifiable pieceLocations = new SeeminglyUnmodifiable();
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() + " ]"); }
}
When you run this example, you see:
运行此示例时,您会看到:
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 ]
The startingLocations map is encapsulated and only returned by leveraging Collections.unmodifiableMap in the getStartingLocations method. However, the scheme is subverted by getting access to any object and then changing it, as seen in "Try 2" in the code above. Suffice to say that one can only rely on Collections.unmodifiableMap to give a truly unmodifiable map IF the objects held in the map are themselves immutable. If they're not, we'd want to either copy the objects in the map or otherwise restrict access to the object's modifier methods, if possible.
startsLocations映射是封装的,只能通过在getStartingLocations方法中利用Collections.unmodifiableMap来返回。但是,通过访问任何对象然后更改它来破坏该方案,如上面代码中的“尝试2”所示。可以说,如果地图中保存的对象本身是不可变的,那么只能依靠Collections.unmodifiableMap来提供真正不可修改的地图。如果不是,我们要么想要复制地图中的对象,要么限制访问对象的修改方法,如果可能的话。
#3
-2
Wrapping the Map is to ensure the caller won't change the collection. While this is useful in testing, you really should find this sort of bug there, it may not be so useful in production. A simple workaround is to have your own wrapper like.
包装地图是为了确保调用者不会更改集合。虽然这在测试中很有用,但你真的应该在那里找到这种bug,它在生产中可能不那么有用。一个简单的解决方法是拥有自己的包装器。
public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> map) {
assert (map = Collections.unmodifiableMap(map)) != null;
return map;
}
This only wraps the map when assertions are turned on.
这仅在断言打开时包装地图。