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

StackOverflow https://stackoverflow.com/questions/3999086

  •  10-10-2019
  •  | 
  •  

Вопрос

У меня есть карта констант, вот такая:

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

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

Это было полезно?

Решение

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

class A {
    private Map importantData;

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

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

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

Другие советы

Выше утверждение Кэмерона Скиннера, что «collections.unmodifiablemap гарантирует, что карта не будет изменена» на самом деле является только частично Верно, в целом, хотя это является точным для конкретного примера в вопросе (только потому, что объект символа неизменен). Я объясню с примером.

Collections.unmodifiablemap фактически дает вам защиту, что использованная литература на объекты, удерживаемые на карте, не могут быть изменены. Это делает это путем ограничения «положить» в карту, которую она возвращает. Тем не менее, исходная инкапсулированная карта все еще может быть изменена извне класса, потому что collections.unmodifiablemap не делает никаких копий содержимого карты.

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

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

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 ]

Карта стартовых списков инкапсулируется и возвращается только путем использования collections.unmodifiablemap в методе GetStartingLocations. Тем не менее, схема подрывается, получая доступ к любому объекту, а затем изменяя его, как видно из «Try 2» в приведенном выше коде. Достаточно сказать, что можно полагаться на collections.unmodifiablemap, чтобы дать по -настоящему немодифицируемую карту, если объекты, удерживаемые на карте, сами по себе неизменны. Если это не так, мы хотели бы либо скопировать объекты в карте, либо иным образом ограничить доступ к методам модификатора объекта, если это возможно.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top