Capacidade inicial de ArrayList e IndexOutOfBoundsException

Considere este código de exemplo:

List myList = new ArrayList(7); myList.add(5, "Hello"); myList.removeAll(Collections.singleton(null)); System.out.println(myList.size() + " objects:" ); for (String s : myList) { System.out.println("\t" + s); } 

myList é inicializado com uma capacidade inicial de 7, depois a próxima linha tenta adicionar a String “Hello” na posição 5. Isso lança um IndexOutOfBoundsException:

Exceção no thread “main” java.lang.IndexOutOfBoundsException: Index: 5, Size: 0

Eu examinei essa questão sobre o que significa “capacidade inicial” em termos de uma ArrayList. Eu entendo que este construtor em particular está alocando espaço para 7 elementos de String, e se tentarmos adicionar 8 elementos à lista, teremos que alocar mais espaço.

O que não entendo é por que não cria uma lista “vazia” de tamanho 7, com valores nulos em cada índice, semelhante ao que aconteceria se declarássemos String[] myArray = new String[7] . Eu me lembro de aprender que ArrayList é a implementação de um array dynamic em Java, então eu esperaria um tipo similar de comportamento. Se eu realmente não tenho espaço para 7 Strings alocadas quando eu declarar new ArrayList(7) , o que está realmente acontecendo?

O que não entendo é por que não cria uma lista “vazia” de tamanho 7, com valores nulos em cada índice, semelhante ao que aconteceria se declarássemos String [] myArray = new String [7].

Isso seria útil em alguns casos … e não seria útil em outros. Muitas vezes você tem um limite superior do tamanho da lista que você vai criar (ou pelo menos um palpite), mas então você o preenche … e você não quer ter uma lista que então tenha o tamanho errado. .. então você teria que manter um índice enquanto “definia” valores e, em seguida, definir o tamanho depois.

Eu me lembro de aprender que ArrayList é a implementação de um array dynamic em Java, então eu esperaria um tipo similar de comportamento.

Não, não é mesmo. É uma lista que pode ser redimensionada e usa um array nos bastidores. Tente não pensar nisso como uma matriz.

Se eu realmente não tenho espaço para 7 Strings alocadas quando eu declarar novo ArrayList(7) , o que está realmente acontecendo?

Você tem espaço para referências de 7 cordas. O tamanho do buffer (ou seja, a capacidade) é de pelo menos 7, mas o tamanho lógico da lista ainda é 0 – você não adicionou nada a ele. É como se você tivesse uma folha de papel longa o suficiente para 7 linhas, mas você ainda não escreveu nada.

Se você quiser uma lista pré-preenchida, você pode facilmente escrever um método para criar um:

 public static List createPrefilledList(int size, T item) { ArrayList list = new ArrayList(size); for (int i = 0; i < size; i++) { list.add(item); } } 

Há uma diferença entre a capacidade inicial de uma matriz e seu tamanho (ou seja, o número de elementos que sua matriz contém). Seu tamanho é usado para determinar se você está tentando acessar um índice que está fora dos limites.

Aqui está o método ArrayList.java que faz esta verificação:

  private void rangeCheckForAdd(int index) { if (index < 0 || index > this.size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } 

Como você pode ver, não tem nada a ver com a capacidade inicial da matriz. Baseia-se unicamente no número de elementos que contém.

A capacidade inicial só faz uma coisa: dá uma sugestão (não um requisito) de quão grande deve ser o array de apoio. Logicamente, não há diferença entre quais operações são permitidas com ou sem a sugestão ou qual é a sugestão. As únicas mudanças serão em quais operações internas podem ocorrer ou não como uma sugestão de otimização.

Você só pode “adicionar” assim a posições que já existem na matriz. Os elementos antes da posição 5 ainda não existem, por isso lança uma exceção. Do Javadoc :

Lança: IndexOutOfBoundsException – se o índice estiver fora do intervalo (index <0 || index> size ())