wake-up-neo.com

Warum ist die Standardkapazität von ArrayList in Java 8 jetzt null?

Wie ich mich erinnere, war vor Java 8 die Standardkapazität von ArrayList 10.

Überraschenderweise lautet der Kommentar zum Standardkonstruktor (void) weiterhin: Constructs an empty list with an initial capacity of ten.

Von ArrayList.Java:

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

...

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
80
kevinarpe

Technisch gesehen ist es 10, nicht Null, wenn Sie eine verzögerte Initialisierung des Backing-Arrays zulassen. 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);
}

woher

/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

Sie beziehen sich nur auf das anfängliche Array-Objekt der Größe Null, das von allen anfänglich leeren ArrayList-Objekten gemeinsam genutzt wird. D.h. Die Kapazität von 10 ist garantiert lazily, eine Optimierung, die auch in Java 7 vorhanden ist.

Zwar ist der Vertrag mit dem Bauunternehmer nicht ganz genau. Vielleicht ist das 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 mit der Standardgröße erstellt wird. Laut unserem Performance-Analyse-Team werden ungefähr 85% der ArrayList-Instanzen mit einer Standardgröße erstellt. Diese Optimierung gilt also für die überwiegende Mehrheit der Fälle. 

Bei HashMap wird das Schwellenwertfeld zum Nachverfolgen der angeforderten Anfangsgröße verwendet, 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 (Tabelle == EMPTY_TABLE) verwendet, um die Notwendigkeit zu erkennen, das Bucket-Array aufzublasen. In readObject gibt es etwas mehr Arbeit, um eine effiziente Anfangskapazität zu wählen.

From: http://mail.openjdk.Java.net/pipermail/core-libs-dev/2013-April/015585.html

97
Lukas Eder

In Java 8 ist die Standardkapazität von ArrayList 0, bis wir dem ArrayList-Objekt mindestens ein Objekt hinzufügen (Sie können es Lazy-Initialisierung nennen). Siehe Code unten für Hilfe.

ArrayList al = new ArrayList();          //Size:  0, Capacity:  0
ArrayList al = new ArrayList(5);         //Size:  0, Capacity:  5
ArrayList al = new ArrayList(new ArrayList(5)); //Size:  0, Capacity:  0
al.add( "shailesh" );                    //Size:  1, Capacity: 10

public static void main( String[] args )
        throws Exception
    {
        ArrayList al = new ArrayList();
        getCapacity( al );
        al.add( "shailesh" );
        getCapacity( al );
    }

    static void getCapacity( ArrayList<?> l )
        throws Exception
    {
        Field dataField = ArrayList.class.getDeclaredField( "elementData" );
        dataField.setAccessible( true );
        System.out.format( "Size: %2d, Capacity: %2d%n", l.size(), ( (Object[]) dataField.get( l ) ).length );
}

Response: - 
Size:  0, Capacity:  0
Size:  1, Capacity: 10

Nun ist die Frage, warum diese Änderung in Java 8 vorgenommen wurde. 

Antwort ist, um Speicherplatz zu sparen. Millionen von Array-Listenobjekten werden in Echtzeit-Java-Anwendungen erstellt. Die Standardgröße von 10 Objekten bedeutet, dass wir dem zugrundeliegenden Array bei der Erstellung 10 Zeiger (40 oder 80 Byte) zuweisen und diese mit Nullen füllen .. Ein leeres Array (mit Nullen gefüllt) belegt viel Speicherplatz.

Bei der Lazy-Initialisierung wird dieser Speicherverbrauch auf den Moment verschoben, bis Sie die Array-Liste tatsächlich verwenden.

Artikel Standardkapazität von ArrayList in Java 8 erläutert die Details.

11

Wenn die allererste Operation, die mit einer ArrayList durchgeführt wird, darin besteht, addAll eine Auflistung zu übergeben, die mehr als zehn Elemente enthält, dann würde jeder Aufwand, der zum Erstellen eines ersten zehn Elemente umfassenden Arrays zum Halten des ArrayList-Inhalts erforderlich ist, aus dem Fenster geworfen. Wenn einer ArrayList etwas hinzugefügt wird, muss geprüft werden, ob die Größe der Ergebnisliste die Größe des Sicherungsspeichers überschreitet. Wenn der anfängliche Sicherungsspeicher die Größe null anstelle von zehn hat, wird dieser Test während der Lebensdauer einer Liste, deren erster Vorgang ein "Hinzufügen" ist, ein weiteres Mal fehlschlagen, was das Erstellen des anfänglichen Zehn-Elemente-Arrays erfordern würde, aber dies kostet Dies ist weniger als die Kosten für die Erstellung eines Arrays mit zehn Elementen, das sich nie als gewohnt erweist.

Es wurde gesagt, dass es in einigen Kontexten möglich gewesen wäre, die Leistung weiter zu verbessern, wenn "addAll" überladen wurde, wobei angegeben wurde, wie viele Elemente (falls vorhanden) wahrscheinlich nach dem aktuellen Eintrag zur Liste hinzugefügt werden könnten und welche Verwenden Sie das, um das Allokationsverhalten 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 benötigt. Es gibt viele Situationen, in denen eine Liste einmal gefüllt und danach nicht mehr geändert wird. Wenn der Code an dem Punkt weiß, dass die endgültige Größe einer Liste 170 Elemente sein wird, diese 150 Elemente und einen Hintergrundspeicher der Größe 160 aufweist, ist es nicht hilfreich, den Hintergrundspeicher auf die Größe 320 zu bringen und die Größe 320 zu belassen 170 ist weniger effizient als die nächste Zuteilung auf 170 zu erhöhen.

6
supercat

Die Frage ist "warum?".

Speicherprofil-Inspektionen (zum Beispiel ( https://www.yourkit.com/docs/Java/help/inspections_mem.jsp#sparse_arrays ) zeigen, dass leere Arrays (gefüllt mit Nullen) tonnenweise Speicher belegen. 

Die Standardgröße von 10 Objekten bedeutet, dass wir dem Array bei der Erstellung 10 Zeiger (40 oder 80 Byte) zuweisen und diese mit Nullen füllen. Echte Java-Anwendungen erstellen Millionen von Array-Listen.

Durch die eingeführte Änderung wird ^ W entfernt, um den Speicherplatzverbrauch zu verschieben, bis Sie die Array-Liste tatsächlich verwenden.

3
ya_pulser

Nach der obigen Frage ging ich ArrayList Document of Java 8 durch. Ich fand, dass die Standardgröße immer noch 10 ist.

 Please see below

0
Rahul Maurya

Die Standardgröße von ArrayList in Java 8 ist noch 10. Die einzige in Java 8 vorgenommene Änderung besteht darin, dass, wenn ein Coder Elemente mit weniger als 10 Elementen hinzufügt, die verbleibenden leeren Felder der Arrayliste nicht mit null angegeben werden. Um es so zu sagen, weil ich selbst diese Situation durchgemacht habe und Eclipse mich in diese Änderung von Java 8 hineinziehen ließ.

Sie können diese Änderung anhand des Screenshots begründen. Darin sehen Sie, dass die ArrayList-Größe in Object [10] mit 10 angegeben ist, die Anzahl der angezeigten Elemente jedoch nur 7 beträgt. Rest-Nullwert-Elemente werden hier nicht angezeigt. In Java 7 ist der Screenshot mit einer einzigen Änderung identisch. Dies bedeutet, dass auch die Nullwertelemente angezeigt werden, für die der Codierer Code schreiben muss, um Nullwerte zu verarbeiten, wenn er die vollständige Arrayliste durchläuft, während in Java 8 diese Belastung entfernt wird der Leiter des Programmierers/Entwicklers.

Screenshot Link.

0
tech_lover