J'ai une question de base sur Java ArrayList
.
Lorsque ArrayList
est déclaré et initialisé à l'aide du constructeur par défaut, l'espace mémoire de 10 éléments est créé. Maintenant, quand j'ajoute un onzième élément, que se passe-t-il? Un nouvel espace mémoire sera-t-il créé avec une capacité de 20 (ou plus) éléments (cela nécessite de copier les éléments du premier emplacement mémoire vers le nouvel emplacement) OR quelque chose d'autre?
J'ai vérifié ici . Mais je n'ai pas trouvé de réponse.
S'il vous plaît partager les connaissances. Merci.
Un nouveau tableau est créé et le contenu de l'ancien est copié. C'est tout ce que vous savez au niveau de l'API. Citant de les docs (mon emphase):
Chaque instance de
ArrayList
a une capacité. La capacité est la taille du tableau utilisé pour stocker les éléments dans la liste. Il est toujours au moins aussi grand que la taille de la liste. Lorsque des éléments sont ajoutés à une liste de tableaux, sa capacité augmente automatiquement. Les détails de la politique de croissance ne sont pas spécifiés au-delà du fait que l'ajout d'un élément a un coût en temps amorti constant.
Pour ce qui est de la manière dont cela se passe réellement avec une implémentation spécifique de ArrayList
(telle que celle de Sun), dans leur cas, vous pouvez voir les détails sanglants dans le source. Mais bien sûr, s’appuyer sur les détails d’une implémentation spécifique n’est généralement pas une bonne idée ...
JDK6 de Sun:
Je crois que cela passe à 15 éléments. Pas de codage, mais en regardant le code de grow () dans le jdk.
int newCapacity then = 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);
}
Dans la Javadoc, il est indiqué que cela provient de Java 2 et ainsi de suite. Il s’agit donc d’une valeur sûre dans le JDK de Sun.
EDIT : pour ceux qui n'ont pas compris quel est le lien entre le facteur de multiplication 1.5
et int newCapacity = oldCapacity + (oldCapacity >> 1);
>>
Est un opérateur de décalage à droite qui réduit un nombre à sa moitié. Ainsi,int newCapacity = oldCapacity + (oldCapacity >> 1);
=> int newCapacity = oldCapacity + 0.5*oldCapacity;
=> int newCapacity = 1.5*oldCapacity ;
Cela dépendra de l'implémentation, mais du code source de Sun Java 6:
int newCapacity = (oldCapacity * 3)/2 + 1;
C'est dans la méthode ensureCapacity
. D'autres implémentations de JDK peuvent varier.
Lorsque nous essayons d'ajouter un objet à l'arraylist,
vérifications Java pour vous assurer que le tableau existant a une capacité suffisante pour contenir le nouvel objet. Sinon, un n nouveau tableau de taille supérieure est créé, le l'ancien tableau est copié dans un nouveau tablea en utilisant Arrays.copyOf et le nouveau tableau est assigné au tableau existant.
Regardez le code ci-dessous (tiré de Java Code ArrayList sur GrepCode.com).
Modifier:
public ArrayList () Construit une liste vide avec une capacité initiale de dix.
public ArrayList (int initialCapacity) nous pouvons spécifier la capacité initiale.
public ArrayList (Collection c) Construit une liste contenant les éléments de la collection spécifiée, dans l’ordre dans lequel ils sont renvoyés par l’itérateur de la collection.
Maintenant, lorsque nous utilisons le constructeur ArrayList (), nous obtenons un ArrayList avec Size = 1 Lors de l'ajout d'un 11ème élément dans la liste, un nouvel Arraylist est créé dans la méthode EnsureCapacity ().
En utilisant la formule suivante:
int newCapacity= (oldCapacity * 3)/2 +1;
lorsqu'une ArrayList est déclarée et initialisée à l'aide du constructeur par défaut, l'espace mémoire de 10 éléments est créé. maintenant, quand j'ajoute le 11 ème élément, ce qui se passe est
ArrayList crée un nouvel objet de la taille suivante
i.e OldCapacity * 3/2 + 1 = 10 * 3/2 + 1 = 16
En général, la mémoire double des conteneurs de type ArrayList est augmentée. Ainsi, si vous aviez initialement un espace pour 10 éléments et que vous en avez ajouté 10, le onzième élément sera ajouté à un nouveau tableau (interne) de 20 éléments. La raison en est que le coût incrémentiel de l'ajout d'éléments est réduit de O (n ^ 2) si le tableau avait été incrémenté par incréments de taille fixe en un Nice O(n) lors du doublement de la taille chaque fois que le tableau interne est plein.
Jusqu'au JDK 6, la capacité augmente avec la formule newCapacity = (oldCapacity * 3/2) + 1
.
Dans la version 7 et les versions ultérieures de JDK, la formule devient newCapacity = oldCapacity + (oldCapacity >> 1)
.
Donc, si la capacité initiale est 10
alors la nouvelle capacité sera 16 in JDK6
et 15 in above JDK6
Regardons ce code (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) Placez le point d'arrêt sur la ligne lorsque "dernier" est inséré
2) Allez à la méthode add de ArrayList
vous verrez
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
3) Aller à la méthode EnsureCapacityInternal cet appel de méthode ensureExplicitCapacity
4)
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
return true;
Dans notre exemple, minCapacity est égal à 11 11-10 > 0
Donc vous avez besoin de la méthode grow
5)
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);
}
Permet de décrire chaque étape:
1) oldCapacity
= 10 car nous n'avons pas mis ce paramètre lorsque ArrayList
était init, il utilisera donc la capacité par défaut (10)
2) int newCapacity = oldCapacity + (oldCapacity >> 1);
newCapacity est égal à oldCapacity plus oldCapacity avec un décalage droit de un (oldCapacity is 10
Ceci est la représentation binaire 00001010
Se déplaçant d'un bit à droite, nous obtiendrons 00000101
Qui est 5 en décimal donc newCapacity
est 10 + 5 = 15
)
3)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
Par exemple, votre capacité initiale est 1, lorsque vous ajoutez le deuxième élément à arrayList, newCapacity
sera égal à 1(oldCapacity) + 0 (moved to right by one bit) = 1
. Dans ce cas, newCapacity est inférieure à minCapacity et elementData
(array object à l'intérieur de arrayList) ne peut pas contenir de nouvel élément, donc newCapacity est égal à minCapacity
4)
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
Vérifiez si la taille du tableau atteint MAX_ARRAY_SIZE (qui est Integer.MAX - 8) Pourquoi la taille maximale du tableau de ArrayList est Integer.MAX_VALUE - 8?
5) Enfin, il copie les anciennes valeurs dans le newArray d’une longueur de 15.
Contexte Java 8
Je donne ma réponse ici dans le contexte de Oracle Java 8 implémentation, puisqu’après avoir lu toutes les réponses, j’ai trouvé une réponse dans le contexte de Java 6 a donné par gmgmiller, et une autre réponse a été donnée dans le contexte de Java 7. Mais comment Java 8 implémente l'augmentation de taille n'a pas été donnée.
Dans Java 8, le comportement de l'augmentation de la taille est identique à Java 6, voir la méthode grow
de 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);
}
le code clé est cette ligne:
int newCapacity = oldCapacity + (oldCapacity >> 1);
Donc, le facteur de croissance est également égal à 1,5, le même que Java 6.
Lorsque ArrayList est déclaré et initialisé à l'aide du constructeur par défaut, de l'espace mémoire pour 10 éléments est créé.
NON. Lorsque ArrayList est initialisé, l'allocation de mémoire est faite pour un tableau vide. L'allocation de mémoire pour la capacité par défaut (10) est effectuée uniquement lors de l'ajout du premier élément à 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;
P.S. N'ayant pas assez de réputation pour commenter une question, je suggère cela comme une réponse, car personne n'a signalé cette hypothèse incorrecte plus tôt.
La taille de ArrayList
augmente avec n+n/2+1
toujours.
Qu'est-ce qui se passe est qu'un nouveau tableau est créé avec n * 2 espaces, puis tous les éléments de l'ancien tableau sont copiés et le nouvel élément est inséré dans le premier espace libre. Globalement, cela se traduit par O(N)) plus de temps pour la liste de tableaux.
Si vous utilisez Eclipse, installez Jad et Jadclipse pour décompiler les fichiers JAR contenus dans la bibliothèque. Je l'ai fait pour lire le code source d'origine.
ArrayList augmente la taille du facteur de charge dans les cas suivants:
Contexte: JDK 7
Lors de l'ajout d'un élément dans le ArrayList
, le public ensureCapacityInternal
les appels et les autres appels de méthode privés ont lieu en interne pour augmenter la taille. C'est ce qui augmente dynamiquement la taille de ArrayList
. lorsque vous visualisez le code, vous pouvez comprendre la logique en nommant des conventions, raison pour laquelle je n’ajoute pas de description explicite.
public 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);
}
En Jdk 1.6: Nouvelle capacité = (Capacité actuelle * 3/2) + 1;
En Jdk 1.7:
int j = i + (i >> 1); c'est la même chose que Nouvelle capacité = (capacité actuelle * 1/2) + capacité actuelle;
ex: la taille augmentera comme: 10 -> 15 -> 22 -> 33
La capacité par défaut de ArrayList est de 10. Une fois que la capacité atteint sa capacité maximale, la taille de la classe ArrayList sera de 16, lorsque la capacité atteindra sa capacité maximale de 16, la taille de la classe ArrayList sera de 25 et continuera à augmenter en fonction de la taille des données. ...
Comment? Voici la réponse et la formule
New capacity = (Current Capacity * 3/2) + 1
Donc, si la capacité par défaut est 10, alors
Current Capacity = 10
New capacity = (10 * 3/2) + 1
Output is 16
static int getCapacity(ArrayList<?> list) throws Exception {
Field dataField = ArrayList.class.getDeclaredField("elementData");
dataField.setAccessible(true);
return ((Object[]) dataField.get(list)).length;
}
utilisez la méthode ci-dessus pour vérifier la taille lorsque la liste est modifiée.