2010-03-24 10 views
87

Le raccolte Java memorizzano solo oggetti, non tipi primitivi; tuttavia possiamo memorizzare le classi wrapper.Perché le raccolte Java non memorizzano direttamente i tipi di primitive?

Perché questo vincolo?

+2

Questo vincolo fa schifo quando si gestiscono le primitive e si desidera utilizzare le code da inviare e le velocità di invio sono molto veloci. Mi sto occupando di questo problema al momento dell'autoboxing che impiega troppo tempo. – JPM

risposta

73

Era una decisione di progettazione Java, e uno che considera un errore. I contenitori vogliono che Oggetti e primitive non derivino da Object.

Questo è un posto che i progettisti .NET hanno imparato dalla JVM e implementato tipi di valore e generici tali che la boxe è stata eliminata in molti casi. In CLR, i contenitori generici possono memorizzare tipi di valore come parte della struttura del contenitore sottostante.

Java ha scelto di aggiungere il supporto generico al 100% nel compilatore senza il supporto di JVM. Essendo JVM quello che è, non supporta un oggetto "non oggetto". I generici Java ti permettono di fingere che non ci sia un wrapper, ma tu paghi comunque il prezzo della boxe. Questo è IMPORTANTE per alcune classi di programmi.

La boxe è un compromesso tecnico e ritengo che il dettaglio dell'attuazione sfugga al linguaggio. Autoboxing è un buon zucchero sintattico, ma è comunque una penalità per le prestazioni. Se non altro, mi piacerebbe che il compilatore mi avvisasse quando si autoboxes. (Per quanto ne so, potrebbe ora, ho scritto questa risposta nel 2010).

Una buona spiegazione su SO sulla boxe: Why do some languages need Boxing and Unboxing?

E le critiche dei generici Java: Why do some claim that Java's implementation of generics is bad?

In difesa di Java, è facile guardare indietro e criticare. La JVM ha resistito alla prova del tempo ed è un buon progetto sotto molti aspetti.

+0

+1 per i tipi di valore. – Thilo

+5

Non un errore, un tradeoff scelto con cura che credo abbia servito molto bene a Java. – DJClayworth

+12

È stato un errore che .NET ha imparato da esso e ha implementato l'autoboxing dall'inizio e generici a livello di VM senza sovraccarico di boxe. Il tentativo di Java di effettuare una correzione era solo una soluzione a livello di sintassi che soffre ancora del colpo di prestazioni di autoboxing rispetto a nessun boxing. L'implementazione di Java ha mostrato scarse prestazioni con strutture di dati di grandi dimensioni. – codenheim

7

C'è il concetto di auto-boxing e auto-unboxing. Se si tenta di memorizzare int in un List<Integer>, il compilatore Java lo convertirà automaticamente in un Integer.

+0

Penso che sia stato aggiunto dopo java 1.5 ..right? – JavaUser

+1

Il boxing automatico è stato introdotto in Java 1.5 insieme a Generics. – Jeremy

+1

Ma è una cosa a tempo di compilazione; sintassi di zucchero senza prestazioni vantaggiose. I box auto del compilatore Java, quindi la penalizzazione delle prestazioni rispetto alle implementazioni VM come .NET, i cui generici non implicano il pugilato. – codenheim

3

Non è proprio un limite?

Considerare se si desidera creare una raccolta che memorizza i valori primitivi. Come scriveresti una collezione in grado di memorizzare int, o float o char? Molto probabilmente finirai con più raccolte, quindi avrai bisogno di un intlist e una charlist ecc.

Sfruttando la natura orientata agli oggetti di Java quando scrivi una classe di raccolta, può archiviare qualsiasi oggetto in modo che sia necessario un solo classe di raccolta. Questa idea, il polimorfismo, è molto potente e semplifica enormemente la progettazione delle librerie.

+6

"Come scriveresti una collezione in grado di memorizzare int, o float o char?"- Con generici/template correttamente implementati come altri linguaggi che non pagano la pena di fingere che tutto sia un oggetto – codenheim

+0

Non ho quasi mai visto in sei anni di Java la necessità di archiviare una collezione di primitivi, anche nei pochi casi in cui potrei Ho desiderato il tempo extra e i costi di spazio per l'utilizzo degli oggetti di riferimento sono stati trascurabili.In particolare, trovo che molte persone pensano di volere Map , dimenticando che un array lo farà molto bene – DJClayworth

+0

@DJClayworth Funziona bene solo se i valori chiave non sono sparsi. Naturalmente, è possibile utilizzare un array ausiliario per tenere traccia dei tasti, ma questo ha i suoi problemi: un accesso relativamente efficiente richiederebbe la separazione di entrambi gli array in base all'ordine delle chiavi per consentire la ricerca binaria, che a sua volta renderebbe l'inserimento e la cancellazione inefficienti a meno che l'inserimento/la cancellazione non sia modellato in modo tale che gli elementi inseriti rischiano di finire qui un elemento precedentemente cancellato era e/o alcuni buffer sono sparpagliati negli array, ecc. Ci sono risorse disponibili, ma sarebbe bello avere in Java stesso. – JAB

9

E 'una combinazione di due fatti:

  • Java tipi primitivi non sono di riferimento tipo (ad esempio, un int non è una Object)
  • Java fa generici utilizzando il tipo-cancellazione dei tipi di riferimento (ad esempio, un List<?> è davvero un List<Object> in fase di esecuzione)

Poiché entrambe sono vere, le raccolte Java generiche non possono memorizzare direttamente i tipi primitivi. Per praticità, è stata introdotta la funzione di autoboxing per consentire ai tipi primitivi di essere inseriti automaticamente come tipi di riferimento. Non fare errori su di esso, tuttavia, le collezioni stanno ancora memorizzando i riferimenti agli oggetti a prescindere.

Questo potrebbe essere stato evitato? Forse.

  • Se un int è un Object, allora non c'è bisogno di tipi di finestre a tutti.
  • Se i generici non vengono eseguiti utilizzando la cancellazione del tipo, le primitive potrebbero essere state utilizzate per i parametri di tipo.
14

Rende l'implementazione più semplice. Poiché le primitive Java non sono considerate oggetti, è necessario creare una classe di raccolta separata per ciascuna di queste primitive (nessun codice modello da condividere).

È possibile farlo, ovviamente, basta vedere GNU Trove, Apache Commons Primitives o HPPC.

A meno che non si abbiano raccolte veramente grandi, il sovraccarico per i wrapper non è abbastanza importante per le persone a cui prestare attenzione (e quando si hanno raccolte primitive molto grandi, si potrebbe voler impiegare lo sforzo per guardare usando/costruire uno specialista struttura dati per loro).

+0

grazie per l'apache commons primitives e gnu trove menzioni. Link utili –

0

Penso che potremmo vedere i progressi in questo spazio nel JDK possibilmente in Java 10 basato su questo JEP - http://openjdk.java.net/jeps/218.

Se si desidera evitare le primitive di inscatolamento nelle raccolte oggi, ci sono diverse alternative di terze parti. Oltre alle opzioni di terze parti citate in precedenza, sono disponibili anche Eclipse Collections, FastUtil e Koloboke.

Un confronto di mappe primitive è stato pubblicato qualche tempo fa con il titolo: Panoramica HashMap di grandi dimensioni: JDK, FastUtil, Goldman Sachs, HPPC, Koloboke, Trove. La libreria GS Collections (Goldman Sachs) è stata migrata su Eclipse Foundation ed ora è Eclipse Collections.

Problemi correlati