2013-07-05 12 views
7

Direttamente dal this java doc:La mappa contiene se stessa come valore;

Un caso particolare di questo divieto è che non è ammissibile per una mappa di contenere in sé come una chiave. Mentre è ammesso che una mappa sia contenga sé stesso come valore, si consiglia cautela estrema: i metodi uguali e il metodo di hash non sono più ben definiti su tale mappa.

Perché il codice hash e gli uguali non sono più ben definiti su tale mappa?

Grazie in anticipo.

+2

se ricordo bene il metodo equals controlla se il contenuto delle mappe sono uguali. e questo viene fatto usando il metodo equals dei contenuti, cioè la mappa stessa. Sono abbastanza sicuro che finiremo con StackOverflowError –

+0

Potrebbe essere il modo in cui una mappa è uguale all'aspetto della funzione? forse causerà un ciclo infinito? Solo un pensiero – Kevin

+0

@MarcoForberg Se ricordo bene, è consigliabile verificare se l'oggetto con cui si sta confrontando è effettivamente te stesso e restituire true immediatamente se è così. –

risposta

2

La citazione completa del paragrafo dalla documentazione di Java è:

Nota: grande cura deve essere esercitata se gli oggetti mutabili sono utilizzati come chiavi mappa . Il comportamento di una mappa non viene specificato se il valore di un oggetto viene modificato in un modo che influisce su confronti uguali mentre l'oggetto è una chiave nella mappa. Un caso speciale di questo divieto è che non è consentito che una mappa si contenga come chiave.Mentre è consentito che una mappa si contenga come valore, si consiglia un'estrema cautela: i metodi equals e hashCode non sono più ben definiti su tale mappa.

Il metodo AbstractMap.hashCode() utilizza il codice hash delle coppie di valori chiave nella mappa per calcolare un codice hash. Pertanto il codice hash generato da questo metodo cambierebbe ogni volta che la mappa viene modificata.

Il codice hash viene utilizzato per calcolare il bucket per inserire una nuova voce. Se la mappa è stata utilizzata come chiave all'interno di se stessa, il bucket calcolato sarebbe diverso ogni volta che una nuova voce viene aggiornata/rimossa/modificata. Pertanto, le ricerche future con la mappa come chiave molto probabilmente falliranno perché un bucket diverso viene calcolato dal codice hash. Future put potrebbe non essere in grado di rilevare che la chiave è già presente nella mappa e quindi consentire più voci che hanno la stessa chiave (ma in diversi bucket)

1

Due mappe sono uguali se le stesse chiavi mappano gli stessi valori. (In alcune implementazioni.) Quindi per controllare l'uguaglianza, l'uguaglianza di ogni membro dovrebbe essere controllata.

Pertanto, se una mappa contiene se stessa, si otterrebbe una ricorsione infinita di controlli di uguaglianza.

Lo stesso vale per gli hash, in quanto possono essere calcolati in base agli hash degli elementi nella mappa.

Esempio:

Map<Int, Object> ma; 
Map<Int, Object> mb; 
Map<Int, Object> mc; 

ma.put(1, ma); 
ma.put(2, mb); 
mc.put(1, ma); 
mc.put(2, mb); 

Come essere umano, possiamo vedere ma e mc sono uguali dalla definizione. Un computer vedrebbe 2 mappe su mb (una mappa vuota) in entrambe le mappe, il che è positivo. Vedrebbe 1 mappe su un'altra mappa sia in mc che in ma. Controlla se queste mappe sono uguali. Per determinarlo, controlla nuovamente se i due valori per 1 sono uguali. E di nuovo.

Si noti che questo non è il caso per tutte le implementazioni. Alcune implementazioni potrebbero controllare l'uguaglianza nella posizione in memoria dell'oggetto salvato, ... Ma ogni controllo ricorsivo verrà ripetuto all'infinito.

5

Le pertinenti AbstractMap.equals formano parte che viene utilizzato dalla maggior parte delle implementazioni Mappa:

  Iterator<Entry<K,V>> i = entrySet().iterator(); 
      while (i.hasNext()) { 
       Entry<K,V> e = i.next(); 
       K key = e.getKey(); 
       V value = e.getValue(); 
       if (value == null) { 
        if (!(m.get(key)==null && m.containsKey(key))) 
         return false; 
       } else { 
        if (!value.equals(m.get(key))) // would call equals on itself. 
         return false; 
       } 
      } 

Aggiunta mappa come valore si tradurrebbe in un ciclo infinito.

+1

La prima istruzione nel metodo equals() è ** if (o == this) restituisce true; ** Penso che ciò impedirebbe il ciclo infinato, poiché ritornerebbe immediatamente e non eseguirà mai il codice che hai evidenziato. –

+0

Accidenti hai ragione. La risposta deve essere deselezionata. Forse è perché è così dipendente dall'implementazione che non lo hanno definito, non lo consiglio. – ssindelar

+0

bene è possibile creare un problema creando due mappe, inserire una mappa nell'altra e invocare equals su una mappa con l'altro come parametro. oppure semplicemente inserisci una mappa in se stessa e chiama hashcode() –

0

Per cercare di spiegarlo:

Il metodo è uguale a iterare entrambe le mappe e chiamare il metodo equals di ogni tasto e il valore della mappa. Quindi, se una mappa contiene se stessa, continuerai a chiamare il metodo equals a tempo indeterminato.

La stessa cosa accade con il codice hash.

Fonte: codice sorgente della classe AbstractMap

Problemi correlati