Когда unmodifiablemap (действительно) необходим?

у меня есть карта констант, например:

private static Map<String, Character> _typesMap =
        new HashMap<String, Character>() {
        {
            put ("string", 'S');
            put ("normalizedString", 'N');
            put ("token", 'T');
            // (...)
        }

мне действительно нужно использовать Collections.unmodifiableMap() чтобы создать эту карту? В чем преимущество его использования? Есть ли какие-то недостатки в том, чтобы не использовать его, кроме очевидного факта, что они на самом деле не становятся постоянными?

3 ответов


сборники.unmodifiableMap гарантирует, что карта не будет изменена. Это в основном полезно, если вы хотите вернуть только для чтения представление внутренней карты из вызова метода, e.g:

class A {
    private Map importantData;

    public Map getImportantData() {
        return Collections.unmodifiableMap(importantData);
    }
}

Это дает вам быстрый метод, который не рискует, что клиент изменит ваши данные. Это намного быстрее и эффективнее памяти, чем возврат копии карты. Если клиент действительно хочет изменить возвращаемое значение, то они могут скопировать его сами, но изменения в копии не будет отражено в данных А.

Если вы не возвращаете ссылки на карту кому-либо еще, не беспокойтесь о том, чтобы сделать ее неизменяемой, если вы не параноик о том, чтобы сделать ее неизменяемой. Вы, вероятно, можете доверять себе, чтобы не изменить его.


заявление Кэмерона Скиннера выше этого " коллекции.unmodifiableMap гарантирует, что карта не будет изменена " на самом деле только частично true в целом, хотя это бывает точным для конкретного примера в вопросе (только потому, что объект символа неизменяем). Я объясню на примере.

сборники.unmodifiableMap фактически только дает вам защиту, что ссылки к объектам, удерживаемым на карте, нельзя измененный. Он делает это, ограничивая " put " в карте, которую он возвращает. Однако исходная инкапсулированная карта все еще может быть изменена извне класса, поскольку коллекции.unmodifiableMap не делает никаких копий содержимого карты.

в вопросе написал Пауло, объекты символ в карты к счастью неизменяемым. Однако в целом это может быть не так, и неизменяемость рекламируется коллекциями.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() + " ]");       }
}

когда вы запускаете этот пример, вы видите:

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 ]

карта startingLocations инкапсулируется и возвращается только при использовании коллекций.unmodifiableMap в методе getStartingLocations. Однако схема подрывается, получая доступ к любому объекту, а затем изменяя его, как показано в "Try 2" в коде выше. Достаточно сказать,что можно полагаться только на коллекции.unmodifiableMap давала немодифицируемая карта, если объекты, содержащиеся в карте, сами неизменяемы. Если это не так, мы хотели бы либо скопировать объекты на карте, либо по возможности ограничить доступ к методам модификатора объекта.


обертывание карты должно гарантировать, что вызывающий абонент не изменит коллекцию. Хотя это полезно при тестировании, вы действительно должны найти такого рода ошибку, это может быть не так полезно в производстве. Простой обходной путь-иметь свою собственную оболочку.

public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> map) {
   assert (map = Collections.unmodifiableMap(map)) != null;
   return map;
}

это только обертывает карту, когда утверждения включены.