2010-10-22 22 views
30

Tengo un mapa de las constantes, así:¿Cuándo es necesario el mapa no modificable (realmente)?

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 obvio de que realmente no se están volviendo constantes?

+3

¿No te gusta 'final' tampoco? –

+20

final no hará que el mapa sea inmutable, solo la referencia al mapa. Todavía puede llamar a métodos (como 'poner') en las referencias finales. –

+0

En este ejemplo, 'final' es necesario. (A menos que '_typesMap' se restablezca a un mapa diferente más adelante ...) –

Respuesta

59

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 en cuanto a la memoria que devolver una copia del mapa. Si el cliente realmente desea modificar el valor devuelto, entonces ellos pueden copiarlo ellos mismos, pero los cambios a la copia no se reflejarán en los datos de A.

Si no devuelve las referencias del mapa a nadie más, no se moleste en hacerlo inmodificable a menos que sea paranoico sobre hacerlo inmutable. Probablemente puedas confiar en ti mismo para no cambiarlo.

+3

"Es mucho más rápido y más eficiente con la memoria que devolver una copia del mapa". - Eso es lo que quería saber. :-) –

-2

Envasar el mapa es garantizar que la persona que llama no cambiará la colección. Si bien esto es útil en las pruebas, realmente debería encontrar este tipo de error allí, puede que no sea tan útil en la producción. Una solución simple es tener tu propia envoltura.

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

Esto solo ajusta el mapa cuando las aserciones están activadas.

+0

"puede no ser tan útil en la producción" - ¿por qué? Ese es mi punto, ¿es útil? es realmente necesario? –

+0

Si lo prueba correctamente en un entorno de prueba, esto no debería ser necesario en producción porque ha comprobado que la colección nunca se modifica. –

+1

A menos que haya probado todas las rutas de código en su entorno de prueba, no puede estar seguro. Si pierde una ruta de código, generalmente es mejor fallar rápidamente (incluso en producción) que dejar que la colección se modifique y causar un problema difícil de depurar más adelante en la línea. – Kelvin

24
declaración de

Cameron Skinner anteriormente que "garantiza Collections.unmodifiableMap que el mapa no será modificado" es en realidad sólo en 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 personaje el objeto es inmutable). Voy a explicar con un ejemplo.

Collections.unmodifiableMap en realidad solo le ofrece protección que las referencias a los objetos que se encuentran en el mapa no se pueden cambiar. Lo hace al restringir el 'put' en el mapa que devuelve. Sin embargo, el mapa encapsulado original aún se puede modificar desde fuera de la clase porque Collections.unmodifiableMap no realiza ninguna copia del contenido del mapa.

En la pregunta publicada por Paulo, los objetos de caracteres que se encuentran en el mapa son afortunadamente no modificables. Sin embargo, en general, esto puede no ser cierto y la no modificabilidad anunciada por Collections.unmodifiableMap no debe ser la única salvaguarda. Por ejemplo, mira 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, ver:

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 ] 

Los startingLocations mapa se encapsula y sólo regresó al aprovechar Collections.unmodifiableMap en el método getStartingLocations. Sin embargo, el esquema se subvierte al obtener acceso a cualquier objeto y luego cambiarlo, como se ve en "Probar 2" en el código anterior. Basta con decir que solo se puede confiar en Collections.unmodifiableMap para proporcionar un mapa verdaderamente no modificable SI los objetos que se encuentran en el mapa son inmutables. Si no es así, querríamos copiar los objetos en el mapa o restringir el acceso a los métodos modificadores del objeto, si es posible.

+0

gracias por dejarlo en claro – charany1

Cuestiones relacionadas