Utilização da Classe ArrayList do Java para Manipulação de Vetores
Tags: java, arraylist, array, list
Published: 2022-10-27 17:54:14

Neste artigo vou apresentar algumas características da classe **ArrayList** do Java e demonstrar a utilização de alguns dos métodos mais utilizados.
## Por que utilizar a classe ArrayList?
Quando utilizamos o array padrão do Java, precisamos determinar qual será o seu tamanho no momento de sua criação. Esse tamanho é fixo e, uma vez criado, não conseguimos alterar o número de elementos. Se por exemplo, precisarmos adicionar mais um elemento, é necessário criar um novo array e copiar todos os elementos que estão no antigo array para o novo. Essa prática acaba sendo muito trabalhosa e também provoca uma perda de desempenho.

A classe ArrayList serve justamente para solucionar esse problema, pois ela permite que sejam criados arrays dinâmicos, ou seja, se precisarmos adicionar mais um elemento, o ArrayList faz esse redimensionamento de forma automática.

##Hierarquia da Classe ArrayList

A classe **ArrayList** faz parte do **Java Collections**, que pertence ao pacote **java.util**. O ArrayList é uma implementação da interface **List**, que por sua vez estende a interface **Collection**. No diagrama abaixo, as linhas cheias representam “**extends**” e as linhas pontilhadas “**implements**”:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hb9enhg4urvaghhivdmt.png)

Além dos métodos herdados de Collection, a interface List fornece métodos que permitem a manipulação de elementos com base na posição (ou índice) em que eles se encontram. Assim como nos vetores, o índice começa em zero.

##Alguns dos principais métodos

- **add(item)** -> adiciona um novo elemento ao ArrayList. O elemento é inserido no final da lista;
- **add(posição, item)** -> insere o elemento em uma posição específica da lista;
- **remove(posição)** -> remove o elemento de uma determinada posição da lista;
- **set(posição, item)** -> substitui um elemento em uma posição específica;
- **get(posição)** -> retorna o elemento da posição especificada;
- **indexOf(item)** -> retorna a posição da primeira ocorrência do elemento especificado;
- **contains(item)** -> retorna verdadeiro caso a lista contenha o elemento especificado. Caso contrário, retorna falso;
- **clear()** -> remove todos os elementos da lista;
- **isEmpty()**-> retorna verdadeiro se a lista estiver vazia. Caso contrário retorna falso;
- **size()**-> retorna a quantidade de elementos da lista.

##Formas de inicializar um ArrayList

Podemos inicializar o ArrayList sem especificar o tipo dos elementos que a lista conterá, como mostrado a seguir:

```java
ArrayList lista = new ArrayList();
```

Esse formato permite que adicionemos elementos de qualquer tipo na lista. No exemplo abaixo, repare que os tipos dos dados inseridos estão misturados:

```java
import java.util.ArrayList;

public class TesteArrayList {
    public static void main(String[] args) {
        ArrayList lista = new ArrayList();
        lista.add("Ana");
        lista.add(123);
        lista.add("Maria");
        lista.add(12.34);
        lista.add("Paulo");

        System.out.println(lista);
    }
}

```
Saída:

```
[Ana, 123, Maria, 12.34, Paulo]
```

Se por exemplo, precisássemos armazenar o elemento de determinada posição em uma variável, seria necessário fazer o **_type cast_**, indicando o tipo do Objeto. No exemplo abaixo, estamos tentando armazenar o elemento da **posição 1** na **String nome**, no entanto esse elemento é um valor inteiro(123), o que acabará causando um **_“run time exception”_** no momento da execução.

```java
import java.util.ArrayList;

public class TesteArrayList {
    public static void main(String[] args) {
        ArrayList lista = new ArrayList();
        lista.add("Ana");
        lista.add(123);
        lista.add("Maria");
        lista.add(12.34);
        lista.add("Paulo");

        // vai causar run time exception, 
        // pois o elemento da posição 1 é um inteiro(123)
        String nome = (String) lista.get(1); 

        System.out.println(lista);
    }
}

```
Saída:

```
java.lang.ClassCastException
class java.lang.Integer cannot be cast to class java.lang.String
```

Portanto, para evitar problemas, o ideal é não permitirmos que o ArrayList armazene elementos com tipos misturados. Para limitar o ArrayList podemos usar o **Generics**, inserindo o tipo de objeto que a lista irá armazenar.
 
**Obs.**: Não são aceitos tipos primitivos. Por exemplo, para inserir valores inteiros, usamos **Integer** ao invés de int.

```java
ArrayList<Integer> lista = new ArrayList<>();
```
Veja um exemplo em que criamos uma lista de elementos inteiros.

```java
import java.util.ArrayList;

public class TesteArrayList {
    public static void main(String[] args) {
        ArrayList<Integer> lista = new ArrayList<>();
        lista.add(1);
        lista.add(2);
        lista.add(3);

        System.out.println(lista);
    }
}

```
Saída:

```
[1, 2, 3] 
```
Podemos também utilizar o **polimorfismo** e declarar uma referência da interface **List** e instanciar a classe **ArrayList**.

```java
List<Integer> lista = new ArrayList<>();
```
A vantagem de declararmos o ArrayList desta maneira é que podemos a qualquer momento, caso seja necessário, alterar a classe que implementa a interface **List**. Por exemplo, ao invés de utilizar a classe ArrayList podemos optar por usar a **LinkedList** ou a **Vector**, pois ambas implementam a interface **List**.

```java
List<Integer> lista = new LinkedList<>();
```

Caso já saibamos quantos elementos o array terá, podemos passar a capacidade inicial por parâmetro no construtor. Isso é muito recomendado nestes casos, pois evitamos desperdício de memória. No exemplo abaixo, estamos iniciando nosso ArrayList com 5 posições. Caso seja necessário adicionar mais elementos posteriormente, o redimensionamento é feito de forma automática.

```java
List<String> lista = new ArrayList<>(5);
```
Podemos ainda criar uma lista a partir de outra já existente. Basta passarmos a lista base por parâmetro no construtor:

```java
List<String> lista = new ArrayList<>(5);
lista.add("Maria");
lista.add("Pedro");
List<String> novaLista = new ArrayList<>(lista);
```
##Exemplo de como utilizar ArrayList
No exemplo a seguir, será demonstrado como usar alguns dos métodos fornecidos pela classe ArrayList.

1. Primeiro, vamos instanciar uma lista; 
2. Depois, vamos adicionar cinco cidades usando o método **add(item)**; 
3. Percorreremos a lista e exibiremos essas cidades usando o **for**. Aqui serão utilizados os métodos **get(posição)** e **size()**;
4. Com o método **remove(posição)**, removeremos a cidade da posição 3 (Manaus); 
5. Com o método **add(posição, item)**, adicionaremos a cidade “Florianópolis” na posição zero;
6. Vamos percorrer novamente a lista usando **for-each**;
7. Agora usaremos o **indexOf(item)** para retornar a posição da cidade “Recife” e, com o método **set(posição, item)** substituiremos “Recife” por “Salvador”;
8. Agora vamos percorrer a lista novamente, mas usando **Iterator**. (O Iterator é um padrão de projeto que permite iterar entre os elementos dentro da coleção).

```java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TesteArrayList {
    public static void main(String[] args) {

        // 1) Primeiro, vamos instanciar uma lista;
        List<String> cidades = new ArrayList<>();

        // 2) Depois, vamos adicionar cinco cidades 
        // usando o método add(item);
        cidades.add("São Paulo");
        cidades.add("Rio de Janeiro");
        cidades.add("Recife");
        cidades.add("Manaus");
        cidades.add("Curitiba");

        // 3) Percorreremos a lista e exibiremos 
        // essas cidades usando o for.
        // Aqui serão utilizados os métodos get(posição) e size()
        System.out.println("Percorrendo a lista usando for:");
        for(int i = 0; i < cidades.size(); i++){
            System.out.println("Posição " + i + " - " + cidades.get(i));
        }

        // 4) Com o método remove(posição), 
        // removeremos a cidade da posição 3 (Manaus);
        cidades.remove(3);

        // 5) Com o método add(posição, item), 
        // adicionaremos a cidade “Florianópolis” na posição zero;
        cidades.add(0, "Florianópolis");

        // 6) Vamos percorrer novamente a lista usando for-each;
        System.out.println("==================");
        System.out.println("Percorrendo a lista usando for-each:");
        int i = 0;
        for(String cidade : cidades){
            System.out.println("Posição " + i + " - " + cidade);
            i++;
        }

        // 7) Agora usaremos o indexOf(item) para retornar 
        // a posição da cidade “Recife” e, com o método 
        // set(posição, item) substituiremos "Recife" 
        // por “Salvador”;
        int posicaoCidade = cidades.indexOf("Recife");
        cidades.set(posicaoCidade, "Salvador");

        // 8) Agora vamos percorrer a lista novamente, 
        // mas usando Iterator.
        System.out.println("==================");
        System.out.println("Percorrendo a lista usando Iterator:");
        i = 0;
        Iterator<String> iterator = cidades.iterator();
        // o método hasNext() verifica se existe 
        // um próximo elemento para ser iterado
        while (iterator.hasNext()){
            // o método next() retorna o próximo elemento
            System.out.println("Posição " + i + " - " + iterator.next()); 
            i++;
        }
    }
}

```
A saída após executar a aplicação será:

```
Percorrendo a lista usando for:
Posição 0 - São Paulo
Posição 1 - Rio de Janeiro
Posição 2 - Recife
Posição 3 - Manaus
Posição 4 - Curitiba
==================
Percorrendo a lista usando for-each:
Posição 0 - Florianópolis
Posição 1 - São Paulo
Posição 2 - Rio de Janeiro
Posição 3 - Recife
Posição 4 - Curitiba
==================
Percorrendo a lista usando Iterator:
Posição 0 - Florianópolis
Posição 1 - São Paulo
Posição 2 - Rio de Janeiro
Posição 3 - Salvador
Posição 4 - Curitiba

```
##Vantagens e Desvantagens do ArrayList
O uso do ArayList é mais indicado quando queremos apenas adicionar elementos em uma lista para posteriormente fazer a iteração. Adicionar elementos ao final de uma lista é bastante simples (isso caso a capacidade de armazenamento do array não tenha sido ultrapassado). O acesso a qualquer elemento do array pelo seu índice também é feito de forma bastante eficiente, ou seja, quando usamos o método **get(posição)**, o tempo para acessar um elemento que esteja no início, ou no meio, ou no fim é praticamente o mesmo, independente do tamanho do vetor.

No entanto, o ArrayList na verdade usa o array padrão internamente, ou seja, quando a capacidade do array acaba, ele precisa criar um novo array com capacidade maior e copiar os elementos do antigo para o novo. 

Quando usamos, por exemplo, o método add(posição, item) para adicionar um elemento no início ou no meio do vetor, primeiramente será necessário deslocar cada elemento uma posição para a direita, fazendo com que a posição desejada seja liberada, e só assim adicionar o novo item nesta posição. Caso o array seja muito grande, isso pode consumir muita memória. 

Remover elementos no meio ou no início de um array também pode ser muito crítico, pois não é permitido existir “buracos” no vetor, sendo necessário deslocar todos os elementos para a esquerda. 

##Considerações Finais

Neste artigo foram abordadas algumas características do **ArrayList**. Vimos que esta classe implementa a interface **List** e faz parte do pacote **java.util**. Também conhecemos alguns dos seus métodos mais utilizados e entendemos um pouco do seu funcionamento, bem como suas vantagens e desvantagens.

Dependendo do contexto, talvez outras implementações sejam mais apropriadas, como **LinkedList** (que também implementa a interface **List**), ou **HashSet** (que implementa a interface **Set**). Mas isso é assunto para um próximo artigo.