Technisch gesehen ist es 10
nicht Null, wenn Sie eine verzögerte Initialisierung des Backing-Arrays zugeben. Sehen:
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
wo
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
Sie beziehen sich nur auf das anfängliche Array-Objekt mit der Größe Null, das von allen anfangs leeren ArrayList
Objekten gemeinsam genutzt wird. Dh die Kapazität von 10
ist träge garantiert , eine Optimierung, die auch in Java 7 vorhanden ist.
Zugegebenermaßen ist der Bauvertrag nicht ganz korrekt. Vielleicht ist dies hier die Quelle der Verwirrung.
Hintergrund
Hier ist eine E-Mail von Mike Duigou
Ich habe eine aktualisierte Version des leeren Patches ArrayList und HashMap veröffentlicht.
http://cr.openjdk.java.net/~mduigou/JDK-7143928/1/webrev/
Diese überarbeitete Implementierung führt keine neuen Felder in eine der Klassen ein. Bei ArrayList erfolgt die verzögerte Zuordnung des Hintergrundarrays nur, wenn die Liste in der Standardgröße erstellt wird. Laut unserem Leistungsanalyseteam werden ungefähr 85% der ArrayList-Instanzen in der Standardgröße erstellt, sodass diese Optimierung für die überwiegende Mehrheit der Fälle gültig ist.
Bei HashMap wird das Schwellenwertfeld kreativ verwendet, um die angeforderte Anfangsgröße zu verfolgen, bis das Bucket-Array benötigt wird. Auf der Leseseite wird der leere Kartenfall mit isEmpty () getestet. Bei der Schreibgröße wird ein Vergleich von (table == EMPTY_TABLE) verwendet, um die Notwendigkeit zu erkennen, das Bucket-Array aufzublasen. In readObject gibt es etwas mehr Arbeit, um eine effiziente Anfangskapazität auszuwählen.
Von: http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-April/015585.html
getCapacity()
Methode oder ähnliches. (Das heißt, so etwasensureCapacity(7)
ist ein No-Op für eine standardmäßig initialisierte ArrayList, also sollten wir wirklich so tun, als ob ihre anfängliche Kapazität wirklich 10 wäre.)ArrayList
mit dem Konstruktor no-arg erstellten Element wiederholt Elemente hinzufügenint
und dem Konstruktor keine Null übergeben , und wenn Sie die interne Arraygröße reflektierend oder in einem Debugger betrachten. Im Standardfall springt das Array von der Länge 0 auf 10 und dann auf 15, 22, gefolgt von der 1,5-fachen Wachstumsrate. Das Übergeben von Null als Anfangskapazität führt zu einem Wachstum von 0 auf 1, 2, 3, 4, 6, 9, 13, 19 ....emptyList()
immer noch weniger Speicher verbraucht als mehrere leereArrayList
Instanzen. Es ist jetzt nur weniger wichtig und wird daher nicht an jedem Ort benötigt , insbesondere nicht an Orten mit einer höheren Wahrscheinlichkeit, Elemente zu einem späteren Zeitpunkt hinzuzufügen. Denken Sie auch daran , dass Sie manchmal wollen eine unveränderliche leere Liste und dannemptyList()
ist der Weg zu gehen.In Java 8 ist die Standardkapazität von ArrayList 0, bis mindestens ein Objekt zum ArrayList-Objekt hinzugefügt wird (Sie können es als verzögerte Initialisierung bezeichnen).
Die Frage ist nun, warum diese Änderung in JAVA 8 vorgenommen wurde.
Die Antwort ist, Speicherplatz zu sparen. Millionen von Array-Listenobjekten werden in Echtzeit-Java-Anwendungen erstellt. Die Standardgröße von 10 Objekten bedeutet, dass wir bei der Erstellung 10 Zeiger (40 oder 80 Byte) für das zugrunde liegende Array zuweisen und diese mit Nullen füllen. Ein leeres Array (gefüllt mit Nullen) belegt viel Speicher.
Durch die verzögerte Initialisierung wird dieser Speicherverbrauch verschoben, bis Sie die Array-Liste tatsächlich verwenden.
Weitere Informationen finden Sie im folgenden Code.
Artikel Die Standardkapazität von ArrayList in Java 8 erläutert dies ausführlich.
quelle
Wenn die allererste Operation, die mit einer ArrayList ausgeführt wird, darin besteht,
addAll
eine Sammlung mit mehr als zehn Elementen zu übergeben, wird jeder Aufwand, der in die Erstellung eines anfänglichen Arrays mit zehn Elementen für den Inhalt der ArrayList investiert wird, aus dem Fenster geworfen. Wenn einer ArrayList etwas hinzugefügt wird, muss getestet werden, ob die Größe der resultierenden Liste die Größe des Sicherungsspeichers überschreitet. Wenn Sie zulassen, dass der anfängliche Sicherungsspeicher die Größe Null anstelle von zehn hat, schlägt dieser Test ein weiteres Mal während der Lebensdauer einer Liste fehl, deren erste Operation ein "Hinzufügen" ist, für das das anfängliche Array mit zehn Elementen erstellt werden muss. Diese Kosten sind jedoch weniger als die Kosten für die Erstellung eines Arrays mit zehn Elementen, das nie verwendet wird.Allerdings wäre es in einigen Kontexten möglicherweise möglich gewesen, die Leistung weiter zu verbessern, wenn eine Überladung von "addAll" aufgetreten wäre, in der angegeben wurde, wie viele Elemente (falls vorhanden) nach dem vorliegenden wahrscheinlich zur Liste hinzugefügt würden und welche Verwenden Sie dies, um das Zuordnungsverhalten zu beeinflussen. In einigen Fällen hat Code, der die letzten Elemente zu einer Liste hinzufügt, eine ziemlich gute Vorstellung davon, dass die Liste darüber hinaus keinen Platz mehr benötigt. Es gibt viele Situationen, in denen eine Liste einmal ausgefüllt und danach nie mehr geändert wird. Wenn der Code zu diesem Zeitpunkt weiß, dass die endgültige Größe einer Liste 170 Elemente beträgt, hat sie 150 Elemente und einen Hintergrundspeicher der Größe 160,
quelle
addAll()
. Dies ist eine weitere Möglichkeit, die Effizienz rund um den ersten Malloc zu verbessern.Die Frage ist 'warum?'.
Inspektionen der Speicherprofilerstellung (z. B. ( https://www.yourkit.com/docs/java/help/inspections_mem.jsp#sparse_arrays ) zeigen, dass leere (mit Nullen gefüllte) Arrays Tonnen von Speicher belegen.
Die Standardgröße von 10 Objekten bedeutet, dass wir bei der Erstellung 10 Zeiger (40 oder 80 Byte) für das zugrunde liegende Array zuweisen und diese mit Nullen füllen. Echte Java-Anwendungen erstellen Millionen von Array-Listen.
Die eingeführte Modifikation entfernt ^ W verschiebt diesen Speicherverbrauch bis zu dem Moment, an dem Sie die Array-Liste tatsächlich verwenden.
quelle
Die Standardgröße von ArrayList in JAVA 8 ist Stil 10. Die einzige in JAVA 8 vorgenommene Änderung besteht darin, dass die verbleibenden leeren Stellen in der Arrayliste nicht mit Null angegeben werden, wenn ein Codierer Elemente mit weniger als 10 hinzufügt. Das zu sagen, weil ich selbst durch diese Situation und die Sonnenfinsternis gegangen bin, hat mich dazu gebracht, mich mit dieser Änderung von JAVA 8 zu befassen.
Sie können diese Änderung rechtfertigen, indem Sie sich den folgenden Screenshot ansehen. Darin können Sie sehen, dass die ArrayList-Größe in Object [10] als 10 angegeben ist, die Anzahl der angezeigten Elemente jedoch nur 7 beträgt. Rest-Nullwertelemente werden hier nicht angezeigt. In JAVA 7 ist der folgende Screenshot mit nur einer einzigen Änderung identisch, dh es werden auch die Nullwertelemente angezeigt, für die der Codierer Code für die Behandlung von Nullwerten schreiben muss, wenn er die vollständige Array-Liste iteriert, während in JAVA 8 diese Belastung beseitigt wird der Leiter des Codierers / Entwicklers.
Screenshot-Link.
quelle
Nach der obigen Frage habe ich das ArrayList-Dokument von Java 8 durchgesehen. Ich habe festgestellt, dass die Standardgröße immer noch nur 10 ist.
quelle