2015-05-13 4 views
11

In Java, è possibile creare un nuovo HashMap per contenere un numero specifico di elementi in questo modo:Differenza tra nuova HashMap (int) e guava Maps.newHashMapWithExpectedSize (int)

Map m = new HashMap(100); 

Guava fornisce un metodo Maps.newHashMapWithExpectedSize(int), che mi aspetterei di chiamare semplicemente HashMap(int). Ma non lo fa, invece calcola la propria capacità e la usa.

Perché lo newHashMapWithExpectedSize fa le sue cose e perché dovrei usarlo chiamando direttamente lo new HashMap(int)?

+0

La mia risposta copre questo, ma il problema fondamentale qui è che l'API JDK è cattiva perché non dimensiona il 'HashMap'" per contenere un numero specifico di elementi "come ci si aspetterebbe sarebbe. Invece lo dimensiona per contenere il 75% del numero specificato. – ColinD

risposta

6

Hai letto il metodo Javadoc?

crea un'istanza HashMap, con una "capacità iniziale" abbastanza alto che dovrebbe detenere expectedSize elementi senza crescita.

Nota che il parametro "dimensione iniziale" del new HashMap(int) del costruttore specifica la dimensione iniziale della tabella di hash che le voci sono memorizzati in, che è fondamentalmente un dettaglio di implementazione che non si dovrebbero avere a cuore. La tabella hash verrà ridimensionata quando supera il fattore di carico della mappa (che per impostazione predefinita è 0,75), il che significa che se si specifica una capacità iniziale di 16 e quindi si aggiungono 16 voci alla mappa, la tabella hash verrà quasi certamente ridimensionata.

Con il metodo di Guava, se si specifica una dimensione atteso di 16 e poi aggiungere 16 voci, la tabella hash dovrebbe non ridimensionamento.

+0

No, il tuo esempio non lo farà. La libreria standard arrotonda le dimensioni fino a 16 (la successiva potenza di 2) e riempiendola di 10 elementi non supererà la soglia per estendere le dimensioni. Tuttavia se hai una dimensione prevista di 13 e la riempia con 13 lo farà. –

+0

Grazie, corretto l'esempio specifico da utilizzare 16. Per dimensioni arbitrarie, è ancora generalmente vero che la tabella potrebbe ridimensionarsi se si specifica una capacità iniziale uguale al numero di voci che si desidera aggiungere. – ColinD

0

Guava semplicemente moltiplica la dimensione passata per 2 (in modo sicuro) e chiama il normale costruttore di hashmap. Ciò lo rende più scarso quindi ci sono meno collisioni durante l'hashing.

La javadoc sul calcolo della capacità indica che calcola un valore per la capacità in modo che l'hashmap sia tra il 25% e il 50% pieno, che è molto lontano dalla soglia che potrebbe innescare un ridimensionamento.

La libreria standard arrotonda la dimensione prevista fino alla potenza più vicina di 2 e la assegna come dimensione, quindi imposta la soglia per il ridimensionamento al 75%. Se avessimo chiesto casualmente delle taglie, la libreria standard si ridimensionerebbe nel 50% dei casi.

Se evitare la soglia sarebbe l'unica considerazione, moltiplicando con 1,34 sarebbe sufficiente disporre di spazio sufficiente per evitare il ridimensionamento sul riempimento con la dimensione prevista degli elementi.

Sembra il tipico rapporto velocità/spazio e gli ingegneri di Google sono più appassionati di velocità e gli ingegneri Sun/Oracle hanno più spazio.

+1

No. Se si chiama 'new HashMap (n)' e quindi si inseriscono n voci, la tabella hash verrà ridimensionata perché non è sufficientemente grande dalla definizione del JDK; Guava sta solo compensando quello. –

+0

Sì, come ha detto Louis, la tabella hash finirà all'incirca con le stesse dimensioni indipendentemente dal metodo che si usa per costruire la 'Mappa ', quindi non ci dovrebbe essere nessuno spazio significativo diverso. L'unica differenza è se la tabella hash debba essere ridimensionata o meno prima che tutte le voci che si desidera aggiungere possano essere aggiunte. – ColinD

+0

sì, hai ragione. Alloca la dimensione arrotondata alla potenza successiva di 2 e imposta il normale fattore di carico del 75%, che verrebbe attivato quando lo si riempie di elementi di dimensioni (se la dimensione è abbastanza vicina al numero arrotondato, o in circa il 50% di casi). Ero convinto erroneamente che la libreria standard lo compensasse. Ancora il numero 2 è piuttosto arbitrario, quindi continuo a credere che anche la scarsità giochi un ruolo. –

1

L'argomento del costruttore HashMap è la capacità della mappa, ovvero il numero di bucket.

Quindi, se si passa 10 come argomento e si memorizzano 8 chiavi nella mappa, verrà raggiunta la soglia di restituzione (75% per impostazione predefinita) e la mappa verrà sottoposta a restituzione.

D'altra parte, l'argomento passato a newHashMapWithExpectedSize() è la dimensione prevista della mappa.Quindi, se superi 10, Guava creerà una mappa con un numero sufficiente di bucket per assicurarsi che la mappa non riduca quando si inseriscono 10 elementi: almeno 14 bucket.

+1

Un 'HashMap' usa la potenza successiva di due come sua capacità. Quindi se imposti 'initialCapacity' su 10, la vera capacità sarà 16. Factoring che per 0,75 genera un totale di 12 voci senza ridimensionamento. – Bubletan

+1

@Bubletan: questo è vero, ma A) è un dettaglio di implementazione che è completamente non specificato dall'API, e B) non cambia il fatto che per molti possibili valori di 'initialCapacity', la tabella dovrà essere ridimensionata e rehashed per aggiungere tante voci. – ColinD

+0

@ColinD concordato, non c'è modo di conoscere la capacità iniziale effettiva da javadoc senza leggere il codice. In realtà è fuorviante in alcuni punti. –