Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

class A {
    private Map importantData;

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

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

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

Ответ 2

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

Collections.unmodifiableMap фактически дает вам защиту, что ссылки на объекты, хранящиеся на карте, не могут быть изменены. Он делает это, ограничивая "put" на карте, которую он возвращает. Однако исходная инкапсулированная карта все еще может быть изменена вне класса, потому что 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 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 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 start is now at [ " + newCamelLoc.getX() +  ", " + newCamelLoc.getY() + " ]");       }
}

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

The LeftCamel start is at [ 1.0, 3.0 ]
Try 1 - Could not update the map!
Try 2 - Map updated! The LeftCamel start is now at [ 0.0, 0.0 ]

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

Ответ 3

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

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

Это только обертывает карту при включении утверждений.