web-dev-qa-db-fra.com

Quand le plan non modifiable est-il (vraiment) nécessaire?

J'ai une carte de constantes, comme ceci:

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

Dois-je vraiment utiliser Collections.unmodifiableMap() pour créer cette carte? Quel est l'avantage de l'utiliser? Y a-t-il des inconvénients à ne pas l'utiliser, outre le fait évident qu'ils ne deviennent pas vraiment constants?

37
Paulo Guedes

Collections.unmodifiableMap garantit que la carte ne sera pas modifiée. Il est surtout utile si vous souhaitez renvoyer une vue en lecture seule d'une carte interne à partir d'un appel de méthode, par exemple:

class A {
    private Map importantData;

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

Cela vous donne une méthode rapide qui ne risque pas que le client modifie vos données. C'est beaucoup plus rapide et plus efficace en mémoire que de renvoyer une copie de la carte. Si le client souhaite vraiment modifier la valeur retournée, il peut le copier lui-même, mais les modifications apportées à la copie ne seront pas reflétées dans les données de A.

Si vous ne renvoyez pas de références de carte à quelqu'un d'autre, ne vous embêtez pas à le rendre non modifiable, sauf si vous êtes paranoïaque à le rendre immuable. Vous pouvez probablement vous faire confiance pour ne pas le changer.

70
Cameron Skinner

La déclaration de Cameron Skinner ci-dessus selon laquelle "Collections.unmodifiableMap garantit que la carte ne sera pas modifiée" n'est en fait que en partie vraie en général, bien qu'elle se révèle exacte pour l'exemple spécifique de la question (uniquement parce que le L'objet de caractère est immuable). Je vais vous expliquer avec un exemple.

Collections.unmodifiableMap ne vous protège en fait que les références aux objets contenus dans la carte ne peuvent pas être modifiés. Il le fait en restreignant le "put" dans la carte qu'il renvoie. Cependant, la carte encapsulée d'origine peut toujours être modifiée de l'extérieur de la classe car Collections.unmodifiableMap ne fait aucune copie du contenu de la carte.

Dans la question posée par Paulo, les objets Personnages contenus dans la carte sont heureusement inchangeables. Cependant, en général, cela peut ne pas être vrai et l'immodifiabilité annoncée par Collections.unmodifiableMap ne devrait pas être la seule garantie. Par exemple, voir l'exemple ci-dessous.

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

Lorsque vous exécutez cet exemple, vous voyez:

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 ]

La carte startingLocations est encapsulée et renvoyée uniquement en tirant parti de Collections.unmodifiableMap dans la méthode getStartingLocations. Cependant, le schéma est inversé en obtenant l'accès à n'importe quel objet et en le modifiant, comme indiqué dans "Try 2" dans le code ci-dessus. Il suffit de dire que l'on ne peut compter que sur Collections.unmodifiableMap pour donner une carte vraiment non modifiable SI les objets contenus dans la carte sont eux-mêmes immuables. Si ce n'est pas le cas, nous souhaitons soit copier les objets dans la carte, soit restreindre l'accès aux méthodes de modification de l'objet, si possible.

30
Don