Pregunta

Tengo un mapa de las constantes, así:

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

¿Es realmente necesario utilizar Collections.unmodifiableMap() para crear este mapa? ¿Cuál es la ventaja de usarlo? ¿Hay alguna desventaja de no usarlo, además del hecho evidente de que en realidad no están llegando a ser constante?

¿Fue útil?

Solución

Collections.unmodifiableMap garantiza que el mapa no se modificará. Es sobre todo útil si desea devolver una vista de sólo lectura de un mapa interno de una llamada a un método, por ejemplo:

class A {
    private Map importantData;

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

Esto le da un método rápido que no ponga en riesgo el cliente cambiar sus datos. Es mucho más rápido y más eficiente de la memoria de devolver una copia del mapa. Si el cliente realmente quiere modificar el valor devuelto entonces pueden copiar por sí mismos, pero los cambios en la copia no se refleja en los datos de A.

Si no está devolviendo referencias mapa a cualquier otra persona entonces no se moleste por lo que es inmodificable a menos que seas paranoico acerca de lo que es inmutable. Probablemente se puede confiar en ti mismo a lo cambie.

Otros consejos

La declaración de Cameron Skinner anteriormente que "garantiza Collections.unmodifiableMap que el mapa no será modificado" es en realidad sólo parte cierto en general, a pesar de que pasa a ser preciso para el ejemplo específico en la cuestión ( sólo porque el objeto del carácter es inmutable). Voy a explicar con un ejemplo.

Collections.unmodifiableMap realidad sólo le da la protección que el referencias a objetos contenidos en el mapa no se puede cambiar. Lo hace mediante la restricción del 'put' en el mapa que devuelve. Sin embargo, el mapa encapsulado original todavía se puede modificar desde fuera de la clase porque Collections.unmodifiableMap no hace ninguna copia de los contenidos del mapa.

En la cuestión publicado por Paulo, el carácter objetos contenidos en el mapa son afortunadamente no se puede modificar. Sin embargo, en general, esto puede no ser cierto y el unmodifiability anunciado por Collections.unmodifiableMap no debe ser la única salvaguardia. Por ejemplo, véase el ejemplo a continuación.

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() + " ]");       }
}

Al ejecutar este ejemplo, se ve:

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 ]

El mapa startingLocations se encapsula y sólo devuelve mediante el aprovechamiento de Collections.unmodifiableMap en el método getStartingLocations. Sin embargo, el esquema es subvertido por conseguir acceso a cualquier objeto y luego cambiarla, como se ve en "Prueba 2" en el código de seguridad. Basta con decir que uno sólo puede depender de Collections.unmodifiableMap para dar un mapa realmente no se puede modificar si los objetos a cabo en el mapa son en sí mismos inmutable. Si no es así, nos gustaría o bien copiar los objetos en el mapa o restringir el acceso a los métodos modificadores del objeto, si es posible.

Envolver el Mapa es asegurar la persona que llama no cambiará la colección. Si bien esto es útil en las pruebas, que realmente debería encontrar este tipo de fallo no, puede que no sea tan útil en la producción. Una solución sencilla es tener su propio envoltorio similares.

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

Esto sólo envuelve el mapa cuando afirmaciones están encendidos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top