collection - lists java
¿Cuándo es necesario el mapa no modificable(realmente)? (3)
Ajustar el mapa es garantizar que la persona que llama no cambie la recopilació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 como.
public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> map) {
assert (map = Collections.unmodifiableMap(map)) != null;
return map;
}
Esto solo envuelve el mapa cuando las aserciones están activadas.
Tengo un mapa de constantes, como este:
private static Map<String, Character> _typesMap =
new HashMap<String, Character>() {
{
put ("string", ''S'');
put ("normalizedString", ''N'');
put ("token", ''T'');
// (...)
}
¿Realmente necesito usar 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?
Collections.unmodifiableMap garantiza que el mapa no se modificará. Es más útil si desea devolver una vista de solo lectura de un mapa interno desde una llamada a un método, por ejemplo:
class A {
private Map importantData;
public Map getImportantData() {
return Collections.unmodifiableMap(importantData);
}
}
Esto le proporciona un método rápido que no arriesga al cliente a 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 la posibilidad de que sea inmutable. Probablemente puedas confiar en ti mismo para no cambiarlo.
La afirmación de Cameron Skinner sobre que "Collections.unmodifiableMap garantiza que el mapa no se modificará" es en realidad parcialmente cierto en general, aunque resulta exacto para el ejemplo específico de la pregunta (solo porque el objeto Character es inmutable). Te lo 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 restringiendo el ''puesto'' 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 del personaje 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, vea 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() + " ]"); }
}
Cuando ejecutas este ejemplo, ves:
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 de startingLocations está encapsulado y solo se devuelve 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.