O construtor usual de ArrayList
é:
ArrayList<?> list = new ArrayList<>();
Mas há também um construtor sobrecarregado com um parâmetro para sua capacidade inicial:
ArrayList<?> list = new ArrayList<>(20);
Por que é útil criar um ArrayList
com uma capacidade inicial quando podemos anexá-lo como quisermos?
java
data-structures
arraylist
capacity
Roubar
fonte
fonte
Respostas:
Se você sabe com antecedência qual será o tamanho da
ArrayList
solução, é mais eficiente especificar a capacidade inicial. Se você não fizer isso, a matriz interna precisará ser realocada repetidamente à medida que a lista aumentar.Quanto maior a lista final, mais tempo você economiza, evitando as realocações.
Dito isto, mesmo sem pré-alocação, é garantido que a inserção de
n
elementos na parte de trás de um tempoArrayList
totalO(n)
. Em outras palavras, anexar um elemento é uma operação de tempo constante amortizada. Isso é obtido fazendo com que cada realocação aumente exponencialmente o tamanho da matriz, geralmente por um fator de1.5
. Com essa abordagem, o número total de operações pode ser demonstradoO(n)
.fonte
O(n log n)
estaria fazendo horários delog n
trabalhon
. Isso é uma superestimação bruta (embora tecnicamente correta com O grande devido ao fato de ser um limite superior). Copia s + s * 1,5 + s * 1,5 ^ 2 + ... + s * 1,5 ^ m (de modo que s * 1,5 ^ m <n <s * 1,5 ^ (m + 1)) elementos no total. Eu não sou bom em somas, então não posso lhe dar a matemática exata em cima da minha cabeça (para redimensionar o fator 2, é 2n, então pode ser 1,5n, dar ou receber uma pequena constante), mas não é preciso olhar muito para ver que essa soma é no máximo um fator constante maior que n. Portanto, são necessárias O (k * n) cópias, o que é obviamente O (n).Porque
ArrayList
é uma estrutura de dados de matriz de redimensionamento dinâmico , o que significa que ela é implementada como uma matriz com um tamanho fixo inicial (padrão). Quando isso for preenchido, a matriz será estendida para uma de tamanho duplo. Como esta operação é cara, você deseja o mínimo possível.Portanto, se você sabe que seu limite superior é de 20 itens, é melhor criar o array com comprimento inicial de 20 do que usar um padrão de, digamos, 15 e redimensioná-lo
15*2 = 30
e usar apenas 20 enquanto desperdiça os ciclos da expansão.PS - Como o AmitG diz, o fator de expansão é específico da implementação (neste caso
(oldCapacity * 3)/2 + 1
)fonte
int newCapacity = (oldCapacity * 3)/2 + 1;
O tamanho padrão da matriz é 10 .
Portanto, se você quiser adicionar 100 ou mais registros, poderá ver a sobrecarga da realocação de memória.
Portanto, se você tem alguma idéia sobre o número de elementos que serão armazenados no Arraylist, é melhor criar o Arraylist com esse tamanho, em vez de começar com 10 e depois aumentá-lo.
fonte
private static final int DEFAULT_CAPACITY = 10
Na verdade, eu escrevi um post sobre o tópico há 2 meses. O artigo é para C #,
List<T>
mas o JavaArrayList
tem uma implementação muito semelhante. ComoArrayList
é implementado usando uma matriz dinâmica, aumenta em tamanho sob demanda. Portanto, o motivo do construtor de capacidade é para fins de otimização.Quando uma dessas operações de redimensionamento ocorre, o ArrayList copia o conteúdo da matriz em uma nova matriz com o dobro da capacidade da antiga. Esta operação é executada em O (n) tempo.
Exemplo
Aqui está um exemplo de como o
ArrayList
tamanho aumentaria:Portanto, a lista começa com uma capacidade de
10
, quando o 11º item é adicionado, é aumentado em50% + 1
para16
. No 17º item, o valorArrayList
é aumentado novamente para25
e assim por diante. Agora considere o exemplo em que estamos criando uma lista em que a capacidade desejada já é conhecida1000000
. Criar oArrayList
construtor sem o tamanho chamaráArrayList.add
1000000
tempos que levam O (1) normalmente ou O (n) no redimensionamento.Compare isso usando o construtor e, em seguida, chamando,
ArrayList.add
que é garantido para executar em O (1) .Java vs C #
Java é como acima, iniciando
10
e aumentando cada redimensionamento em50% + 1
. O C # inicia4
e aumenta muito mais agressivamente, dobrando a cada redimensionamento. O1000000
exemplo adiciona acima para C # usa3097084
operações.Referências
fonte
Definir o tamanho inicial de um ArrayList, por exemplo, para
ArrayList<>(100)
, reduz o número de vezes que a realocação da memória interna deve ocorrer.Exemplo:
Como você pode ver no exemplo acima, um
ArrayList
pode ser expandido, se necessário. O que isso não mostra é que o tamanho da Lista de matriz geralmente dobra (embora observe que o novo tamanho depende da sua implementação). O seguinte é citado da Oracle :Obviamente, se você não tem idéia do tipo de intervalo que estará mantendo, definir o tamanho provavelmente não será uma boa ideia - no entanto, se você tiver um intervalo específico em mente, definir uma capacidade inicial aumentará a eficiência da memória .
fonte
O ArrayList pode conter muitos valores e, ao fazer inserções iniciais grandes, você pode solicitar ao ArrayList que aloque um armazenamento maior para começar, a fim de não desperdiçar os ciclos da CPU ao tentar alocar mais espaço para o próximo item. Assim, alocar algum espaço no início é mais eficiente.
fonte
Isso é para evitar possíveis esforços de realocação para cada objeto.
internamente
new Object[]
é criado.A JVM precisa de esforço para criar
new Object[]
quando você adiciona elemento na lista de matrizes. Se você não tem o código acima (qualquer algo que você pensa) para realocação, em seguida, cada vez que quando você chamararraylist.add()
, em seguida,new Object[]
tem de ser criado que é inútil e estamos perdendo tempo para aumentar o tamanho de 1 para cada objetos a ser adicionado. Portanto, é melhor aumentar o tamanhoObject[]
com a seguinte fórmula.(A JSL usou a fórmula de transmissão fornecida abaixo para aumentar dinamicamente o arraylist em vez de aumentar 1 sempre. Porque para crescer, é necessário um esforço da JVM)
fonte
add
- ele já usa alguma fórmula de crescimento internamente. Portanto, a pergunta não é respondida.int newCapacity = (oldCapacity * 3)/2 + 1;
o que está presente na classe ArrayList. Você ainda acha que não tem resposta?ArrayList
realocação amortizada ocorre em qualquer caso com qualquer valor para a capacidade inicial. E a pergunta é sobre: Por que usar um valor não padrão para a capacidade inicial? Além disso: "ler nas entrelinhas" não é algo desejado em uma resposta técnica. ;-)Eu acho que cada ArrayList é criado com um valor de capacidade init de "10". De qualquer maneira, se você criar um ArrayList sem definir capacidade no construtor, ele será criado com um valor padrão.
fonte
Eu diria que é uma otimização. ArrayList sem capacidade inicial terá ~ 10 linhas vazias e se expandirá quando você estiver adicionando.
Para ter uma lista com exatamente o número de itens que você precisa chamar trimToSize ()
fonte
De acordo com minha experiência com
ArrayList
, fornecer uma capacidade inicial é uma boa maneira de evitar custos de realocação. Mas tem uma ressalva. Todas as sugestões mencionadas acima dizem que só se deve fornecer capacidade inicial quando se conhece uma estimativa aproximada do número de elementos. Mas quando tentamos fornecer uma capacidade inicial sem nenhuma idéia, a quantidade de memória reservada e não utilizada será um desperdício, pois talvez nunca seja necessária uma vez que a lista seja preenchida com o número necessário de elementos. O que estou dizendo é que podemos ser pragmáticos no início enquanto alocamos capacidade e, em seguida, encontrar uma maneira inteligente de saber a capacidade mínima necessária em tempo de execução. ArrayList fornece um método chamadoensureCapacity(int minCapacity)
. Mas então, encontramos uma maneira inteligente ...fonte
Testei ArrayList com e sem initialCapacity e obtive um resultado surpreendente.
Quando defino LOOP_NUMBER para 100.000 ou menos, o resultado é que a configuração de initialCapacity é eficiente.
Mas quando defino LOOP_NUMBER como 1.000.000, o resultado muda para:
Finalmente, eu não conseguia descobrir como isso funciona ?!
Código de amostra:
Eu testei no windows8.1 e jdk1.7.0_80
fonte