简体   繁体   English

什么是不可修改的地图(真的)是必要的?

[英]When is the unmodifiablemap (really) necessary?

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? 我真的需要使用Collections.unmodifiableMap()来创建这个地图吗? 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 guarantees that the map will not be modified. Collections.unmodifiableMap保证不会修改地图。 It's mostly useful if you want to return a read-only view of an internal map from a method call, eg: 如果要从方法调用返回内部映射的只读视图,则最有用,例如:

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. 你可以相信自己不要改变它。

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). Cameron Skinner上面的声明“Collections.unmodifiableMap保证地图不会被修改”实际上通常只是部分正确,尽管它恰好对于问题中的特定示例是准确的(仅因为Character对象是不可变的)。 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. Collections.unmodifiableMap实际上只为您提供保护,即无法更改对地图中保存的对象的引用 It does that by restricting the 'put' into the map that it returns. 它通过将'put'限制在它返回的地图中来实现。 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不会生成映射内容的任何副本。

In the question posted by Paulo, the Character objects held in the map are luckily unmodifiable. 在Paulo发布的问题中,地图中保存的角色对象幸运地无法修改。 However, in general this may not be true and the unmodifiability advertised by Collections.unmodifiableMap should not be the only safeguard. 但是,通常情况可能并非如此,Collections.unmodifiableMap所宣传的不可修改性不应该是唯一的保护措施。 For instance, see the example below. 例如,请参阅下面的示例。

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. startsLocations映射是封装的,只能通过在getStartingLocations方法中利用Collections.unmodifiableMap来返回。 However, the scheme is subverted by getting access to any object and then changing it, as seen in "Try 2" in the code above. 但是,通过访问任何对象然后更改它来破坏该方案,如上面代码中的“尝试2”所示。 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. 可以说,如果地图中保存的对象本身是不可变的,那么只能依靠Collections.unmodifiableMap来提供真正不可修改的地图。 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. 如果不是,我们要么想要复制地图中的对象,要么限制访问对象的修改方法,如果可能的话。

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. 虽然这在测试中很有用,但你真的应该在那里找到这种bug,它在生产中可能没那么有用。 A simple workaround is to have your own wrapper like. 一个简单的解决方法是拥有自己的包装器。

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. 这仅在打开断言时包装地图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 何时使用AtomicReference(Java)? 真的有必要吗? - When to use AtomicReference (Java)? Is it really necessary? 如何配置JVM仅在确实需要时才分配内存? - How to configure JVM to allocate memory only when is really necessary? ServerSocket - 是否真的有必要关闭()它? - ServerSocket - Is it really necessary to close() it? 在这种情况下,仅调整图像大小(png)时,BitmapFactory真的必要吗? - Is BitmapFactory really necessary when it comes to only resizing images (png) in this scenario? 在解析byte []时是否真的需要指定String编码? - Is specifying String encoding when parsing byte[] really necessary? java中的平面图:真的需要lambda吗? - Flatmap in java: is the lambda really necessary? 在Firebase上使用.setPersistenceEnabled(true)时,真的需要.addOnCompleteListener吗? - It's really necessary .addOnCompleteListener when you're using .setPersistenceEnabled(true) on Firebase? 是否真的有必要使用url.openConnection()? - Is it really necessary to use url.openConnection()? Java终结器,真正需要什么任务 - Java Finalizers, what tasks are really necessary 是否真的需要删除完整的侦听器Firebase身份验证? - Is it really necessary to remove the complete listener Firebase Authentication?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM