Warum können Java-Sammlungen Primitivtypen nicht direkt speichern?


123

Java-Sammlungen speichern nur Objekte, keine primitiven Typen. Wir können jedoch die Wrapper-Klassen speichern.

Warum diese Einschränkung?


2
Diese Einschränkung ist schlecht, wenn Sie sich mit Grundelementen befassen und Warteschlangen zum Senden verwenden möchten und Ihre Sendegeschwindigkeiten sehr schnell sind. Ich beschäftige mich gerade mit dem Problem, dass Autoboxing zu lange dauert.
JPM

Technisch gesehen sind primitive Typen Objekte (um genau zu sein Singleton-Instanzen), sie werden nur nicht durch a definiert class, sondern durch die JVM. Die Anweisung int i = 1definiert einen Zeiger auf die Singleton-Instanz des Objekts, die intin der JVM definiert ist , und setzt auf den Wert 1, der irgendwo in der JVM definiert ist. Ja, Zeiger in Java - dies wird nur durch die Sprachimplementierung von Ihnen abstrahiert. Grundelemente können nicht als Generika verwendet werden, da die Sprache voraussagt, dass alle generischen Typen vom Supertyp sein müssen Object- daher wird A<?>zur A<Object>Laufzeit kompiliert .
Robert E Fry

1
@RobertEFry-Primitivtypen sind keine Objekte in Java, daher ist alles, was Sie über Singleton-Instanzen und dergleichen geschrieben haben, grundsätzlich falsch und verwirrend. Ich empfehle, das Kapitel "Typen, Werte und Variablen" der Java-Sprachspezifikation zu lesen , in dem definiert wird, was ein Objekt ist: "Ein Objekt (§4.3.1) ist eine dynamisch erstellte Instanz eines Klassentyps oder ein dynamisch erstelltes Array. ""
Typeracer

Antworten:


99

Es war eine Java-Entwurfsentscheidung, die einige als Fehler betrachten. Container möchten, dass Objekte und Grundelemente nicht von Object abgeleitet werden.

Dies ist ein Ort, den .NET-Designer von der JVM gelernt und Werttypen und Generika so implementiert haben, dass das Boxen in vielen Fällen entfällt. In CLR können generische Container Werttypen als Teil der zugrunde liegenden Container-Struktur speichern.

Java hat sich dafür entschieden, generische Unterstützung zu 100% im Compiler ohne Unterstützung durch die JVM hinzuzufügen. Die JVM, die das ist, was sie ist, unterstützt kein "Nicht-Objekt" -Objekt. Mit Java-Generika können Sie so tun, als gäbe es keinen Wrapper, aber Sie zahlen trotzdem den Leistungspreis für das Boxen. Dies ist für bestimmte Programmklassen WICHTIG.

Boxen ist ein technischer Kompromiss, und ich denke, es handelt sich um Implementierungsdetails, die in die Sprache gelangen. Autoboxing ist ein guter syntaktischer Zucker, aber immer noch ein Leistungsverlust. Wenn überhaupt, möchte ich, dass der Compiler mich warnt, wenn er Autoboxen erstellt. (Soweit ich weiß, habe ich diese Antwort jetzt 2010 geschrieben).

Eine gute Erklärung zu SO über das Boxen: Warum benötigen einige Sprachen Boxen und Unboxen?

Und Kritik an Java-Generika: Warum behaupten manche, dass Javas Implementierung von Generika schlecht ist?

Zu Javas Verteidigung ist es leicht, zurückzublicken und zu kritisieren. Die JVM hat den Test der Zeit überstanden und ist in vielerlei Hinsicht ein gutes Design.


6
Kein Fehler, ein sorgfältig ausgewählter Kompromiss, von dem ich glaube, dass er Java sehr gut gedient hat.
DJClayworth

13
Es war ein ausreichender Fehler, dass .NET von Anfang an daraus gelernt und Autoboxing sowie Generika auf VM-Ebene ohne Box-Overhead implementiert hat. Javas eigener Versuch einer Korrektur war nur eine Lösung auf Syntaxebene, die immer noch unter dem Leistungseinbruch von Autoboxing im Vergleich zu überhaupt keinem Boxen leidet. Die Implementierung von Java hat bei großen Datenstrukturen eine schlechte Leistung gezeigt.
Codenheim

2
@mrjoltcola: IMHO, Standard-Autoboxing ist ein Fehler, aber es hätte eine Möglichkeit geben müssen, Variablen und Parameter zu kennzeichnen, die automatisch die ihnen zugewiesenen Werte enthalten sollten. Selbst jetzt denke ich, dass ein Mittel hinzugefügt werden sollte, um anzugeben, dass bestimmte Variablen oder Parameter keine neu automatisch verpackten Werte akzeptieren sollten [z. B. sollte es legal sein, Object.ReferenceEqualsReferenzen vom Typ zu übergeben, Objectdie Ganzzahlen in Boxen identifizieren, aber es sollte nicht legal sein einen ganzzahligen Wert übergeben]. Javas Auto-Unboxing ist meiner Meinung nach einfach nur böse.
Supercat

18

Erleichtert die Implementierung. Da Java-Grundelemente nicht als Objekte betrachtet werden, müssen Sie für jedes dieser Grundelemente eine separate Auflistungsklasse erstellen (kein gemeinsam zu verwendender Vorlagencode).

Sie können dies natürlich nur unter GNU Trove , Apache Commons Primitives oder HPPC tun .

Wenn Sie nicht über wirklich große Sammlungen verfügen, ist der Aufwand für die Wrapper für die Benutzer nicht wichtig genug (und wenn Sie über wirklich große primitive Sammlungen verfügen, möchten Sie möglicherweise die Mühe aufwenden, eine spezielle Datenstruktur für sie zu verwenden / aufzubauen ).


11

Es ist eine Kombination aus zwei Fakten:

  • Java-Primitivtypen sind keine Referenztypen (z. B. ein intist kein Object)
  • Java erstellt Generika mithilfe der Typlöschung von Referenztypen (z. B. List<?>ist a List<Object>zur Laufzeit wirklich a ).

Da beide zutreffen, können generische Java-Sammlungen primitive Typen nicht direkt speichern. Der Einfachheit halber wird Autoboxing eingeführt, damit primitive Typen automatisch als Referenztypen eingerahmt werden können. Machen Sie keinen Fehler, die Sammlungen speichern trotzdem Objektreferenzen.

Könnte dies vermieden worden sein? Vielleicht.

  • Wenn an ein intist Object, werden Box-Typen überhaupt nicht benötigt.
  • Wenn Generika nicht mit Typlöschung erstellt werden, könnten Primitive für Typparameter verwendet worden sein.

8

Es gibt das Konzept des Auto-Boxing und des Auto-Unboxing. Wenn Sie versuchen, ein intin einem zu speichern, List<Integer>konvertiert der Java-Compiler es automatisch in ein Integer.


1
Autoboxing wurde in Java 1.5 zusammen mit Generics eingeführt.
Jeremy

1
Aber es ist eine Sache zur Kompilierungszeit; Syntax Zucker ohne Leistungsvorteil. Die Autoboxen des Java-Compilers, daher die Leistungseinbußen im Vergleich zu VM-Implementierungen wie .NET, deren Generika kein Boxen beinhalten.
Codenheim

1
@ Mrjoltcola: Was ist dein Punkt? Ich habe nur Fakten geteilt und kein Argument vorgebracht.
Jeremy

3
Mein Punkt ist es wichtig, auf den Unterschied zwischen Syntax und Leistung hinzuweisen. Ich betrachte meine Kommentare auch als Faktenaustausch, nicht als Argument. Vielen Dank.
Codenheim

2

Es ist nicht wirklich eine Einschränkung, oder?

Überlegen Sie, ob Sie eine Sammlung erstellen möchten, in der primitive Werte gespeichert sind. Wie würden Sie eine Sammlung schreiben, die entweder int, float oder char speichern kann? Höchstwahrscheinlich werden Sie mehrere Sammlungen haben, daher benötigen Sie eine Intlist und eine Charlist usw.

Wenn Sie beim Schreiben einer Auflistungsklasse die objektorientierte Natur von Java nutzen, können Sie jedes Objekt speichern, sodass Sie nur eine Auflistungsklasse benötigen. Diese Idee, der Polymorphismus, ist sehr mächtig und vereinfacht das Design von Bibliotheken erheblich.


7
"Wie würden Sie eine Sammlung schreiben, die entweder int, float oder char speichern kann?" - Mit richtig implementierten Generika / Vorlagen wie anderen Sprachen, die nicht die Strafe dafür zahlen, dass alles ein Objekt ist.
Codenheim

Ich habe in sechs Jahren von Java kaum jemals eine Sammlung von Primitiven speichern wollen. Selbst in den wenigen Fällen, in denen ich mir die zusätzlichen Zeit- und Raumkosten für die Verwendung der Referenzobjekte gewünscht hätte, war dies vernachlässigbar. Insbesondere finde ich, dass viele Leute denken, sie wollen Map <int, T> und vergessen, dass ein Array diesen Trick sehr gut macht.
DJClayworth

2
@DJClayworth Das funktioniert nur gut, wenn die Schlüsselwerte nicht spärlich sind. Natürlich könnten Sie ein Hilfsarray verwenden, um die Schlüssel zu verfolgen, aber das hat seine eigenen Probleme: Für einen relativ effizienten Zugriff müssten beide Arrays nach der Schlüsselreihenfolge sortiert werden, um eine binäre Suche zu ermöglichen, die wiederum das Einfügen und Löschen ermöglicht Ineffizient, es sei denn, das Einfügen / Löschen ist so strukturiert, dass eingefügte Elemente wahrscheinlich dort landen, wo sich ein zuvor gelöschtes Element befand, und / oder einige Puffer in den Arrays usw. verteilt sind. Es sind Ressourcen verfügbar, aber es wäre schön, sie zu haben Java selbst.
JAB

@JAB Eigentlich funktioniert es gut, wenn die Schlüssel dünn sind, es braucht nur mehr Speicher als wenn sie nicht dünn sind. Und wenn die Schlüssel spärlich sind, bedeutet dies, dass es nicht viele von ihnen gibt und die Verwendung von Integer als Schlüssel einwandfrei funktioniert. Verwenden Sie den Ansatz, der am wenigsten Speicher benötigt. Oder wie auch immer Sie sich fühlen, wenn es Ihnen egal ist.
DJClayworth

0

Ich denke, wir könnten Fortschritte in diesem Bereich im JDK sehen, möglicherweise in Java 10 basierend auf diesem JEP - http://openjdk.java.net/jeps/218 .

Wenn Sie heute Boxprimitive in Sammlungen vermeiden möchten, gibt es mehrere Alternativen von Drittanbietern. Zusätzlich zu den zuvor erwähnten Optionen von Drittanbietern gibt es auch Eclipse Collections , FastUtil und Koloboke .

Ein Vergleich primitiver Karten wurde vor einiger Zeit auch mit dem Titel veröffentlicht: Große HashMap-Übersicht: JDK, FastUtil, Goldman Sachs, HPPC, Koloboke, Trove . Die Bibliothek der GS-Sammlungen (Goldman Sachs) wurde auf die Eclipse Foundation migriert und heißt jetzt Eclipse-Sammlungen.


0

Der Hauptgrund ist die Java-Designstrategie. ++ 1) Sammlungen erfordern Objekte zur Manipulation und Grundelemente werden nicht vom Objekt abgeleitet, daher kann dies der andere Grund sein. 2) Primitive Java-Datentypen sind beispielsweise keine Referenztypen. int ist kein Objekt.

Überwinden:-

Wir haben ein Konzept von Auto-Boxing und Auto-Unboxing. Wenn Sie also versuchen, primitive Datentypen zu speichern, konvertiert der Compiler diese automatisch in ein Objekt dieser primitiven Datenklasse.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.