Le constructeur habituel de ArrayList
est:
ArrayList<?> list = new ArrayList<>();
Mais il existe aussi un constructeur surchargé avec un paramètre pour sa capacité initiale:
ArrayList<?> list = new ArrayList<>(20);
Pourquoi est-il utile de créer un ArrayList
avec une capacité initiale alors que nous pouvons y ajouter à notre guise?
Réponses:
Si vous savez à l'avance quelle sera la taille de la
ArrayList
, il est plus efficace de spécifier la capacité initiale. Si vous ne le faites pas, le tableau interne devra être réalloué à plusieurs reprises au fur et à mesure que la liste s'allonge.Plus la liste finale est longue, plus vous gagnez de temps en évitant les réallocations.
Cela dit, même sans pré-allocation, l'insertion d'
n
éléments à l'arrière d'unArrayList
est garanti pour prendre unO(n)
temps total . En d'autres termes, l'ajout d'un élément est une opération à temps constant amorti. Pour ce faire, chaque réallocation augmente la taille du tableau de façon exponentielle, généralement d'un facteur de1.5
. Avec cette approche, le nombre total d'opérations peut être démontréO(n)
.la source
O(n log n)
ferait des heures delog n
travailn
. C'est une surestimation grossière (bien que techniquement correcte avec un grand O car il s'agit d'une limite supérieure). Il copie s + s * 1,5 + s * 1,5 ^ 2 + ... + s * 1,5 ^ m (tel que s * 1,5 ^ m <n <s * 1,5 ^ (m + 1)) éléments au total. Je ne suis pas doué pour les sommes, donc je ne peux pas vous donner le calcul précis du haut de ma tête (pour le facteur de redimensionnement 2, c'est 2n, donc ça peut être 1,5n en donnant ou en prenant une petite constante), mais ce n'est pas le cas. Il faut trop plisser les yeux pour voir que cette somme est au plus un facteur constant supérieur à n. Donc, il prend O (k * n) copies, ce qui est bien sûr O (n).Parce qu'il
ArrayList
s'agit d'une structure de données de tableau de redimensionnement dynamique , ce qui signifie qu'elle est implémentée en tant que tableau avec une taille fixe initiale (par défaut). Lorsque celui-ci est rempli, la matrice sera étendue à une double taille. Cette opération est coûteuse, vous en voulez donc le moins possible.Donc, si vous savez que votre limite supérieure est de 20 éléments, il est préférable de créer le tableau avec une longueur initiale de 20 que d'utiliser une valeur par défaut de, disons, 15, puis de le redimensionner
15*2 = 30
et de n'utiliser que 20 tout en gaspillant les cycles d'expansion.PS - Comme le dit AmitG, le facteur d'expansion est spécifique à l'implémentation (dans ce cas
(oldCapacity * 3)/2 + 1
)la source
int newCapacity = (oldCapacity * 3)/2 + 1;
La taille par défaut de Arraylist est de 10 .
Ainsi, si vous comptez ajouter 100 enregistrements ou plus, vous pouvez voir la surcharge de la réallocation de mémoire.
Donc, si vous avez une idée du nombre d'éléments qui seront stockés dans Arraylist, il est préférable de créer Arraylist avec cette taille au lieu de commencer par 10, puis de l'augmenter.
la source
private static final int DEFAULT_CAPACITY = 10
J'ai en fait écrit un article de blog sur le sujet il y a 2 mois. L'article est pour C #
List<T>
mais JavaArrayList
a une implémentation très similaire. Comme ilArrayList
est implémenté à l'aide d'un tableau dynamique, sa taille augmente à la demande. Donc, la raison du constructeur de capacité est à des fins d'optimisation.Lorsqu'une de ces opérations de redimensionnement se produit, ArrayList copie le contenu du tableau dans un nouveau tableau qui est deux fois la capacité de l'ancien. Cette opération s'exécute en temps O (n) .
Exemple
Voici un exemple de la façon dont la
ArrayList
taille augmenterait:Ainsi, la liste commence avec une capacité de
10
, lorsque le 11e élément est ajouté, il est augmenté de50% + 1
à16
. Sur le 17e élément, leArrayList
est à nouveau augmenté25
et ainsi de suite. Prenons maintenant l'exemple où nous créons une liste dans laquelle la capacité souhaitée est déjà connue sous le nom de1000000
. La création duArrayList
constructeur sans la taille appellera desArrayList.add
1000000
temps qui prennent O (1) normalement ou O (n) lors du redimensionnement.Comparez cela en utilisant le constructeur, puis en appelant
ArrayList.add
ce qui est garanti pour s'exécuter dans O (1) .Java contre C #
Java est comme ci-dessus, commençant à
10
et augmentant chaque redimensionnement à50% + 1
. C # commence à4
et augmente beaucoup plus agressivement, doublant à chaque redimensionnement. L'1000000
exemple ajoute ci-dessus pour C # utilise des3097084
opérations.Références
la source
La définition de la taille initiale d'une ArrayList, par exemple à
ArrayList<>(100)
, réduit le nombre de fois que la réallocation de la mémoire interne doit se produire.Exemple:
Comme vous le voyez dans l'exemple ci-dessus, un
ArrayList
peut être développé si nécessaire. Ce que cela ne vous montre pas, c'est que la taille de la liste Arraylist double généralement (mais notez que la nouvelle taille dépend de votre implémentation). Ce qui suit est cité par Oracle :Évidemment, si vous n'avez aucune idée du type de plage que vous tiendrez, définir la taille ne sera probablement pas une bonne idée - cependant, si vous avez une plage spécifique en tête, la définition d'une capacité initiale augmentera l'efficacité de la mémoire. .
la source
ArrayList peut contenir de nombreuses valeurs et lorsque vous effectuez des insertions initiales importantes, vous pouvez indiquer à ArrayList d'allouer un stockage plus important pour commencer afin de ne pas gaspiller de cycles de processeur lorsqu'il tente d'allouer plus d'espace pour l'élément suivant. Ainsi, allouer de l'espace au début est plus efficace.
la source
Ceci afin d'éviter d'éventuels efforts de réallocation pour chaque objet.
new Object[]
est créé en interne .La JVM a besoin d'efforts pour créer
new Object[]
lorsque vous ajoutez un élément dans l'arraylist. Si vous n'avez pas de code ci-dessus (n'importe quel algo que vous pensez) pour la réallocation, alors chaque fois que vous appelez,arraylist.add()
ilnew Object[]
faut créer ce qui est inutile et nous perdons du temps pour augmenter la taille de 1 pour chaque objet à ajouter. Il est donc préférable d'augmenter la taille deObject[]
avec la formule suivante.(JSL a utilisé la formule de prévision donnée ci-dessous pour une arraylist en croissance dynamique au lieu d'augmenter de 1 à chaque fois. Parce que la croissance nécessite des efforts de la part de JVM)
la source
add
- il utilise déjà une formule de croissance en interne. La question n’est donc pas répondue.int newCapacity = (oldCapacity * 3)/2 + 1;
présent dans la classe ArrayList. Pensez-vous toujours qu'il reste sans réponse?ArrayList
la réallocation amortie a lieu en tout cas avec une valeur quelconque pour la capacité initiale. Et la question est la suivante: pourquoi utiliser une valeur non standard pour la capacité initiale? En plus de cela: "lire entre les lignes" n'est pas quelque chose de souhaité dans une réponse technique. ;-)Je pense que chaque ArrayList est créé avec une valeur de capacité d'initialisation de "10". Donc de toute façon, si vous créez une ArrayList sans définir de capacité dans le constructeur, elle sera créée avec une valeur par défaut.
la source
Je dirais que c'est une optimisation. ArrayList sans capacité initiale aura ~ 10 lignes vides et se développera lorsque vous effectuez un ajout.
Pour avoir une liste avec exactement le nombre d'éléments dont vous avez besoin d'appeler trimToSize ()
la source
D'après mon expérience avec
ArrayList
, donner une capacité initiale est un bon moyen d'éviter les coûts de réaffectation. Mais cela mérite une mise en garde. Toutes les suggestions mentionnées ci-dessus indiquent qu'il ne faut fournir la capacité initiale que si une estimation approximative du nombre d'éléments est connue. Mais lorsque nous essayons de donner une capacité initiale sans aucune idée, la quantité de mémoire réservée et inutilisée sera un gaspillage car elle ne sera peut-être jamais nécessaire une fois que la liste est remplie jusqu'au nombre requis d'éléments. Ce que je dis, c'est que nous pouvons être pragmatiques au début lors de l'allocation de capacité, puis trouver un moyen intelligent de connaître la capacité minimale requise au moment de l'exécution. ArrayList fournit une méthode appeléeensureCapacity(int minCapacity)
. Mais alors, il faut trouver un moyen intelligent ...la source
J'ai testé ArrayList avec et sans initialCapacity et j'ai obtenu un résultat surprenant.
Quand je règle LOOP_NUMBER à 100 000 ou moins, le résultat est que le réglage initialCapacity est efficace.
Mais lorsque je règle LOOP_NUMBER sur 1 000 000, le résultat devient:
Enfin, je n'ai pas compris comment ça marche?!
Exemple de code:
J'ai testé sur windows8.1 et jdk1.7.0_80
la source