Java 什么时候需要不可修改的地图(真的)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3999086/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When is the unmodifiablemap (really) necessary?
提问by Paulo Guedes
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()
来创建这张地图吗?使用它有什么好处?除了它们并没有真正变得恒定的明显事实之外,不使用它是否有任何缺点?
采纳答案by 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:
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.
如果您没有将地图引用返回给其他任何人,那么不要费心使其不可修改,除非您对使其不可变感到偏执。你可以相信自己不会改变它。
回答by Peter Lawrey
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.
包装 Map 是为了确保调用者不会更改集合。虽然这在测试中很有用,但您确实应该在那里找到此类错误,但在生产中可能没有那么有用。一个简单的解决方法是拥有自己的包装器。
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.
这仅在断言打开时包装地图。
回答by Don
Cameron Skinner's statement above that "Collections.unmodifiableMap guarantees that the map will not be modified" is actually only partlytrue 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 referencesto 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 实际上只为您提供保护,即不能更改对映射中保存的对象的引用。它通过将“放入”限制到它返回的地图中来做到这一点。但是,原始封装的地图仍然可以从类外部修改,因为 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 发布的问题中,幸运的是地图中的 Character 对象是不可修改的。然而,一般来说这可能不是真的,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.
起始位置映射被封装并且仅通过利用 getStartingLocations 方法中的 Collections.unmodifiableMap 返回。但是,通过访问任何对象然后更改它来破坏该方案,如上面代码中的“尝试 2”所示。可以说,如果映射中的对象本身是不可变的,则只能依靠 Collections.unmodifiableMap 来提供真正不可修改的映射。如果不是,如果可能,我们要么复制地图中的对象,要么限制对对象的修饰符方法的访问。