2010-10-22 3 views
30

Ho una mappa delle costanti, in questo modo:Quando la mappa non modificabile (davvero) necessaria?

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

Ho davvero bisogno di usare Collections.unmodifiableMap() per creare questa mappa? Qual è il vantaggio di usarlo? Ci sono degli svantaggi nel non usarlo, oltre al fatto ovvio che non stanno davvero diventando costanti?

+3

Non ti piace il 'finale' o? –

+20

final non renderà la mappa immutabile, solo il riferimento alla mappa. Puoi ancora chiamare metodi (come 'put') sui riferimenti finali. –

+0

In questo esempio, 'final' è necessario. (A meno che '_typesMap' venga resettato su una mappa diversa più tardi ...) –

risposta

59

Collections.unmodifiableMap garantisce che la mappa non verrà modificata. E 'soprattutto utile se si desidera restituire una vista in sola lettura di una mappa interna da una chiamata di metodo, per esempio:

class A { 
    private Map importantData; 

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

Questo vi dà un metodo rapido che non rischia il cliente cambiare i dati. È molto più veloce e più efficiente in termini di memoria che restituire una copia della mappa. Se il cliente vuole davvero modificare il valore restituito, può copiarlo da sé, ma le modifiche alla copia non si rifletteranno nei dati di A.

Se non si restituiscono riferimenti di mappe a qualcun altro, non preoccuparsi di renderlo non modificabile a meno che non si sia paranoici nel renderlo immutabile. Probabilmente ti puoi fidare di te stesso per non cambiarlo.

+3

"È molto più veloce e più efficiente in termini di memoria che restituire una copia della mappa." - Questo è quello che volevo sapere. :-) –

-2

Il wrapping della mappa serve a garantire che il chiamante non cambi la raccolta. Anche se questo è utile nei test, dovresti trovare questo tipo di bug, potrebbe non essere così utile in produzione. Una soluzione semplice è avere il proprio wrapper come.

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

Questo avvolge solo la mappa quando le asserzioni sono attivate.

+0

"potrebbe non essere così utile in produzione" - perché? Questo è il mio punto, è utile? è davvero necessario? –

+0

Se lo collaudi correttamente in un ambiente di test, questo non dovrebbe essere necessario in produzione perché hai controllato che la raccolta non sia mai stata modificata. –

+1

A meno che non abbiate testato tutti i percorsi di codice nell'ambiente di test, non si può essere sicuri. Se si dimentica un percorso di codice, in genere è meglio fallire rapidamente (anche in produzione) piuttosto che lasciare che la collezione venga modificata e causare un problema difficile da eliminare più avanti. – Kelvin

24
affermazione di

Cameron Skinner sopra che "garantisce Collections.unmodifiableMap che la mappa non sarà modificato" è in realtà solo in parte vero in generale, anche se capita di essere precisi per l'esempio specifico nella questione (solo perché il carattere l'oggetto è immutabile). Spiegherò con un esempio.

Collections.unmodifiableMap fornisce solo la protezione che i riferimenti agli oggetti contenuti nella mappa non possono essere modificati. Lo fa limitando il "put" nella mappa che restituisce. Tuttavia, la mappa incapsulata originale può ancora essere modificata al di fuori della classe, in quanto Collections.unmodifiableMap non esegue alcuna copia del contenuto della mappa.

Nella domanda postata da Paulo, gli oggetti Personaggio contenuti nella mappa sono per fortuna immodificabili. Tuttavia, in generale questo potrebbe non essere vero e l'immodificabilità pubblicizzata da Collections.unmodifiableMap non dovrebbe essere l'unica salvaguardia. Ad esempio, vedi l'esempio qui sotto.

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

Quando si esegue questo esempio, si vede:

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 ] 

I startingLocations mappa è incapsulato e solo restituito sfruttando Collections.unmodifiableMap nel metodo getStartingLocations. Tuttavia, lo schema viene sovvertito ottenendo l'accesso a qualsiasi oggetto e quindi modificandolo, come mostrato in "Prova 2" nel codice sopra. Basti dire che si può solo contare su Collections.unmodifiableMap per dare una mappa veramente non modificabile SE gli oggetti contenuti nella mappa sono di per sé immutabili. Se non lo sono, vorremmo copiare gli oggetti nella mappa o limitare in altro modo l'accesso ai metodi di modifica dell'oggetto, se possibile.

+0

grazie per averlo chiarito – charany1