Ich habe eine grundlegende Frage zu Java ArrayList
.
Wenn ArrayList
mit dem Standardkonstruktor deklariert und initialisiert wird, wird Speicherplatz für 10 Elemente erstellt. Was passiert nun, wenn ich ein elftes Element hinzufüge? Wird neuer Speicherplatz mit einer Kapazität von 20 (oder mehr) Elementen erstellt (dies erfordert das Kopieren von Elementen vom ersten Speicherort an einen neuen Speicherort) ODER etwas anderes?
Ich habe hier nachgesehen . Aber ich habe keine Antwort gefunden.
Bitte teilen Sie das Wissen. Vielen Dank.
java
arrays
arraylist
collections
crazyTechie
quelle
quelle
Antworten:
Ein neues Array wird erstellt und der Inhalt des alten wird kopiert. Das ist alles, was Sie auf API-Ebene wissen. Zitat aus den Dokumenten (mein Schwerpunkt):
In Bezug darauf, wie es tatsächlich mit einer bestimmten Implementierung von
ArrayList
(wie z. B. Suns) passiert , können Sie in diesem Fall die blutigen Details in der Quelle sehen. Aber natürlich ist es normalerweise keine gute Idee, sich auf die Details einer bestimmten Implementierung zu verlassen ...quelle
OutOfMemoryError
. Mir scheint (aus meiner Erfahrung vor Jahren ), dass es im Allgemeinen gelingt, den Fehler auszulösen (wobei etwas Speicher für diesen Zweck reserviert ist), aber YMMV.Suns JDK6:
Ich glaube, dass es auf 15 Elemente wächst. Nicht codieren, sondern den Grow () - Code im JDK betrachten.
int newCapacity dann = 10 + (10 >> 1) = 15.
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
Aus dem Javadoc geht hervor, dass dies aus Java 2 und höher stammt, daher ist es eine sichere Sache im Sun JDK.
EDIT : für diejenigen, die nicht verstanden haben, was der Zusammenhang zwischen Multiplikationsfaktor
1.5
undint newCapacity = oldCapacity + (oldCapacity >> 1);
>>
ist ein Rechtsschaltoperator, der eine Zahl auf die Hälfte reduziert. Alsoint newCapacity = oldCapacity + (oldCapacity >> 1);
=>
int newCapacity = oldCapacity + 0.5*oldCapacity;
=>
int newCapacity = 1.5*oldCapacity ;
quelle
Dies hängt von der Implementierung ab, jedoch vom Sun Java 6-Quellcode:
int newCapacity = (oldCapacity * 3)/2 + 1;
Das liegt in der
ensureCapacity
Methode. Andere JDK-Implementierungen können variieren.quelle
Wenn wir versuchen, der Arrayliste ein Objekt hinzuzufügen,
Java überprüft , ob im vorhandenen Array genügend Kapazität vorhanden ist, um das neue Objekt aufzunehmen. Wenn nicht, wird ein neues Array mit einer größeren Größe erstellt , das alte Array wird mit Arrays.copyOf in ein neues Array kopiert und das neue Array wird dem vorhandenen Array zugewiesen.
Schauen Sie sich den folgenden Code an (entnommen aus Java ArrayList Code bei GrepCode.com).
public ArrayList () Erstellt eine leere Liste mit einer Anfangskapazität von zehn.
public ArrayList (int initialCapacity) können wir die Anfangskapazität angeben.
public ArrayList (Collection c) Erstellt eine Liste mit den Elementen der angegebenen Collection in der Reihenfolge, in der sie vom Iterator der Collection zurückgegeben werden.
Wenn wir jetzt den Konstruktor ArrayList () verwenden, erhalten wir eine ArrayList mit Größe = 10. Beim Hinzufügen des 11. Elements zur Liste wird eine neue Arraylist in der Methode verifyCapacity () erstellt.
Mit folgender Formel:
int newCapacity= (oldCapacity * 3)/2 +1;
quelle
Bis zu JDK 6 wächst die Kapazität mit der Formel
newCapacity = (oldCapacity * 3/2) + 1
.In JDK 7 und höher ändert sich die Formel in
newCapacity = oldCapacity + (oldCapacity >> 1)
.Also , wenn anfängliche Kapazität ist
10
dann neue Kapazität ist16 in JDK6
und15 in above JDK7
quelle
Wenn eine ArrayList mit dem Standardkonstruktor deklariert und initialisiert wird, wird Speicherplatz für 10 Elemente erstellt. Wenn ich nun das 11. Element hinzufüge, passiert Folgendes
ArrayList erstellt ein neues Objekt mit der folgenden Größe
dh OldCapacity * 3/2 + 1 = 10 * 3/2 + 1 = 16
quelle
In der Regel wird der Speicher für Container vom Typ ArrayList durch Verdoppeln vergrößert. Wenn Sie also anfangs Platz für 10 Elemente hatten und 10 hinzugefügt haben, wird das elfte Element einem neuen (internen) Array von 20 Elementen hinzugefügt. Der Grund dafür ist, dass die zusätzlichen Kosten für das Hinzufügen von Elementen von O (n ^ 2) reduziert werden, wenn das Array in Schritten mit fester Größe auf ein schönes O (n) erhöht wurde, wenn die Größe jedes Mal verdoppelt wird, wenn das interne Array voll ist.
quelle
Schauen wir uns diesen Code an (jdk1.8)
@Test public void testArraySize() throws Exception { List<String> list = new ArrayList<>(); list.add("ds"); list.add("cx"); list.add("cx"); list.add("ww"); list.add("ds"); list.add("cx"); list.add("cx"); list.add("ww"); list.add("ds"); list.add("cx"); list.add("last"); }
1) Setzen Sie einen Haltepunkt auf die Linie, wenn "last" eingefügt wird
2) Gehen Sie zur Add-Methode von
ArrayList
Sie werden sehenensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e;
3) Gehen Sie zur Methode sureCapacityInternal, die diese Methode aufruft
ensureExplicitCapacity
4)
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } return true;
In unserem Beispiel ist minCapacity gleich 11,
11-10 > 0
daher benötigen Sie einegrow
Methode5)
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
Beschreiben wir jeden Schritt:
1)
oldCapacity
= 10, da wir diesen Parameter nicht gesetzt haben, alsArrayList
init war, wird daher die Standardkapazität verwendet (10)2)
int newCapacity = oldCapacity + (oldCapacity >> 1);
Hier ist newCapacity gleich oldCapacity plus oldCapacity mit einer Verschiebung nach rechts um eins (oldCapacity is 10
dies ist die binäre Darstellung, die00001010
sich um ein Bit nach rechts bewegt,00000101
was 5 in DezimalzahlnewCapacity
ist10 + 5 = 15
).3)
if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
Zum Beispiel ist Ihre Init-Kapazität 1, wenn Sie das zweite Element zu arrayList hinzufügen
newCapacity
, ist gleich.1(oldCapacity) + 0 (moved to right by one bit) = 1
In diesem Fall ist newCapacity kleiner als minCapacity undelementData
(Array-Objekt in arrayList) kann kein neues Element enthalten, daher ist newCapacity gleich minCapacity4)
if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
Überprüfen Sie, ob die Arraygröße MAX_ARRAY_SIZE (Integer.MAX - 8) erreicht. Warum ist die maximale Arraygröße von ArrayList Integer.MAX_VALUE - 8?
5) Schließlich werden alte Werte mit der Länge 15 in das newArray kopiert
quelle
Ich gebe meine Antwort hier im Kontext der Implementierung von Oracle Java 8, da ich nach dem Lesen aller Antworten festgestellt habe, dass gmgmiller eine Antwort im Kontext von Java 6 gegeben hat und eine andere Antwort im Kontext von Java 7. Es wurde jedoch nicht angegeben, wie Java 8 die Vergrößerung implementiert.
In Java 8 ist das Verhalten zur Vergrößerung der Größe dasselbe wie in Java 6, siehe die
grow
Methode von ArrayList:private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
Der Schlüsselcode ist diese Zeile:
int newCapacity = oldCapacity + (oldCapacity >> 1);
Der Wachstumsfaktor beträgt also eindeutig 1,5, genau wie bei Java 6.
quelle
NEIN. Wenn ArrayList initialisiert wird, wird die Speicherzuordnung für ein leeres Array vorgenommen. Die Speicherzuweisung für die Standardkapazität (10) erfolgt nur nach Hinzufügen des ersten Elements zu ArrayList.
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to * DEFAULT_CAPACITY when the first element is added. */ private transient Object[] elementData;
PS: Ich habe nicht genug Ruf, um eine Frage zu kommentieren, deshalb setze ich dies als Antwort, da niemand zuvor auf diese falsche Annahme hingewiesen hat.
quelle
ArrayList erhöht den Lastfaktor in folgenden Fällen:
Beim Hinzufügen eines Elements zum
ArrayList
werden die folgendenpublic ensureCapacityInternal
Aufrufe und die anderen privaten Methodenaufrufe intern ausgeführt, um die Größe zu erhöhen. Dies ist es, was die Größe von dynamisch erhöhtArrayList
. Während Sie den Code anzeigen, können Sie die Logik verstehen, indem Sie Konventionen benennen. Aus diesem Grund füge ich keine explizite Beschreibung hinzupublic boolean add(E paramE) { ensureCapacityInternal(this.size + 1); this.elementData[(this.size++)] = paramE; return true; } private void ensureCapacityInternal(int paramInt) { if (this.elementData == EMPTY_ELEMENTDATA) paramInt = Math.max(10, paramInt); ensureExplicitCapacity(paramInt); } private void ensureExplicitCapacity(int paramInt) { this.modCount += 1; if (paramInt - this.elementData.length <= 0) return; grow(paramInt); } private void grow(int paramInt) { int i = this.elementData.length; int j = i + (i >> 1); if (j - paramInt < 0) j = paramInt; if (j - 2147483639 > 0) j = hugeCapacity(paramInt); this.elementData = Arrays.copyOf(this.elementData, j); }
quelle
Aus dem JDK-Quellcode habe ich den folgenden Code gefunden
int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);
quelle
Was passiert ist, dass ein neues Array mit n * 2 Leerzeichen erstellt wird, dann werden alle Elemente im alten Array kopiert und das neue Element in das erste freie Leerzeichen eingefügt. Alles in allem führt dies dazu, dass O (N) Zeit für die ArrayList hinzufügt.
Wenn Sie Eclipse verwenden, installieren Sie Jad und Jadclipse , um die in der Bibliothek enthaltenen JARs zu dekompilieren. Ich habe dies getan, um den ursprünglichen Quellcode zu lesen.
quelle
Größe
ArrayList
nimmt mitn+n/2+1
immer zu.quelle
Die Standardkapazität von ArrayList ist 10. Sobald die Kapazität ihre maximale Kapazität erreicht hat, beträgt die Größe der ArrayList 16, sobald die Kapazität ihre maximale Kapazität von 16 erreicht hat, beträgt die Größe der ArrayList 25 und nimmt basierend auf der Datengröße weiter zu. ...
Wie? Hier ist die Antwort und Formel
New capacity = (Current Capacity * 3/2) + 1
Wenn also die Standardkapazität 10 ist, dann
Current Capacity = 10 New capacity = (10 * 3/2) + 1 Output is 16
quelle
static int getCapacity(ArrayList<?> list) throws Exception { Field dataField = ArrayList.class.getDeclaredField("elementData"); dataField.setAccessible(true); return ((Object[]) dataField.get(list)).length; }
Verwenden Sie die oben beschriebene Methode, um die Größe zu überprüfen, wenn die Arrayliste geändert wird.
quelle
In Jdk 1.6: Neue Kapazität = (aktuelle Kapazität * 3/2) + 1;
In Jdk 1.7:
int j = i + (i >> 1); Dies ist dasselbe wie Neue Kapazität = (Aktuelle Kapazität * 1/2) + Aktuelle Kapazität;
Beispiel: Die Größe nimmt zu wie: 10 -> 15 -> 22 -> 33
quelle
(oldSize * 3) / 2 + 1
Wenn Sie den Standardkonstruktor verwenden, ist die Anfangsgröße von
ArrayList
,10
andernfalls können Sie die Anfangsgröße des Arrays beim Erstellen des Objekts von übergebenArrayList
.Beispiel: Falls Standardkonstruktor
List<String> list = new ArrayList<>(); list.size()
Beispiel: Bei parametrisiertem Konstruktor
List<String> list = new ArrayList<>(5); list.size()
quelle
Die Standardgröße der Arrayliste ist 10. Wenn wir die 11. hinzufügen ... Arrayliste erhöht die Größe (n * 2). Die in der alten Arrayliste gespeicherten Werte werden in die neue Arrayliste mit einer Größe von 20 kopiert.
quelle