Java ArrayList

A ArrayList é a coleção mais usada do Java: uma lista de elementos em ordem, cada um numa posição numerada, que cresce e encolhe conforme você acrescenta e remove. Pense nela como um array "elástico". Nesta aula você vai aprender cada operação, uma de cada vez, com as saídas reais e os erros mais comuns.

Você vai aprender
  • Criar uma ArrayList e acrescentar elementos com add (no fim ou numa posição).
  • Ler com get, trocar com set e saber o tamanho com size.
  • Remover por posição e por valor, e a pegadinha do remove com Integer.
  • Procurar com contains e indexOf.
  • Percorrer a lista de três jeitos e ordená-la.
  • O que são IndexOutOfBoundsException e UnsupportedOperationException (com List.of).

Se você caiu aqui direto, leia antes a Visão Geral das Coleções: ela explica por que existem coleções e o que quer dizer List<String> x = new ArrayList<>(). Todas as saídas abaixo vieram do JDK 25, na máquina da Ana.


A ideia: uma lista de compras

Na geladeira da Ana há uma lista de compras. Ela escreve um item embaixo do outro, às vezes encaixa um no meio, risca os que comprou e troca "feijão" por "feijão preto". Cada linha tem uma posição: o primeiro item, o segundo… A ArrayList é exatamente isso, com uma diferença: as posições começam em 0, como nos arrays.

posição:   0        1        2        3
         [arroz] [leite] [feijão] [café]

Por dentro, a ArrayList guarda os elementos num array comum. Quando ele enche, ela cria outro array maior, copia tudo e continua; você nunca vê isso acontecer. Daí o nome: Array (guarda num array) + List (é uma lista).


Criar e acrescentar: add

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> compras = new ArrayList<>();
        System.out.println("Vazia: " + compras + " tamanho " + compras.size());

        compras.add("arroz");
        compras.add("feijão");
        compras.add("café");
        System.out.println("Depois de 3 add: " + compras);

        compras.add(1, "leite");
        System.out.println("add(1, \"leite\"): " + compras);
    }
}
import java.util.ArrayList; import java.util.List;
Traz a classe ArrayList e a interface List do pacote java.util.
List<String> compras
Uma variável chamada compras, do tipo "lista de String". Só textos entram nela.
new ArrayList<>()
Cria a lista vazia. O <> ("diamante") repete o tipo do lado esquerdo.
compras.add("arroz")
add (acrescentar) com um argumento põe o elemento no fim.
compras.add(1, "leite")
Com dois argumentos, o primeiro é a posição: "encaixe leite na posição 1". Quem estava ali e depois anda uma casa para a direita.
$ javac Main.java
$ java Main
Vazia: [] tamanho 0
Depois de 3 add: [arroz, feijão, café]
add(1, "leite"): [arroz, leite, feijão, café]      « feijão e café andaram uma posição

Uma lista vazia aparece como []. Depois do add(1, "leite"), o feijão (que estava na posição 1) foi para a 2, e o café foi da 2 para a 3. A posição pode ir de 0 até size(): add(size(), x) é o mesmo que add(x).

Ler, trocar e contar: get, set, size

Continuando o mesmo programa:

        String primeiro = compras.get(0);
        System.out.println("get(0): " + primeiro);
        System.out.println("get(3): " + compras.get(3));

        String antigo = compras.set(2, "feijão preto");
        System.out.println("set(2, ...) trocou " + antigo + ": " + compras);
        System.out.println("Tamanho: " + compras.size());
MétodoTraduçãoO que fazDevolve
get(i)pegarLê o elemento da posição i, sem tirá-lo da lista. É o lista[i] dos arrays.O elemento
set(i, x)definirTroca o elemento da posição i por x. O tamanho não muda.O elemento antigo
size()tamanhoQuantos elementos há agora.Um int
$ javac Main.java
$ java Main
Vazia: [] tamanho 0
Depois de 3 add: [arroz, feijão, café]
add(1, "leite"): [arroz, leite, feijão, café]
get(0): arroz
get(3): café
set(2, ...) trocou feijão: [arroz, leite, feijão preto, café]   « set devolveu o valor que saiu
Tamanho: 4
add(i, x) encaixa; set(i, x) substitui

Com add(1, "leite") a lista ganhou um elemento (de 3 para 4). Com set(2, "feijão preto") ela continuou com 4: o feijão saiu e o feijão preto entrou no lugar. Trocar um pelo outro é um erro comum.

O último elemento

Como as posições começam em 0, o último está em size() - 1: compras.get(compras.size() - 1). No Java 21 em diante também existe compras.getLast() (e getFirst()) Java 21+.


Remover: por posição ou por valor

O remove tem duas versões, e é aqui que mora a maior pegadinha da ArrayList:

ChamadaRemove…Devolve
remove(int posicao)o elemento daquela posiçãoo elemento removido
remove(Object valor)a primeira ocorrência daquele valortrue se achou e removeu, false se não achou
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> alunos = new ArrayList<>();
        alunos.add("Ana");
        alunos.add("Bruno");
        alunos.add("Carla");
        alunos.add("Bruno");
        System.out.println("Início: " + alunos);

        String saiu = alunos.remove(0);
        System.out.println("remove(0) tirou " + saiu + ": " + alunos);

        boolean achou = alunos.remove("Bruno");
        System.out.println("remove(\"Bruno\") -> " + achou + ": " + alunos);

        boolean achou2 = alunos.remove("Zeca");
        System.out.println("remove(\"Zeca\") -> " + achou2 + ": " + alunos);
    }
}
$ javac Main.java
$ java Main
Início: [Ana, Bruno, Carla, Bruno]
remove(0) tirou Ana: [Bruno, Carla, Bruno]          « por posição: devolveu quem saiu
remove("Bruno") -> true: [Carla, Bruno]             « por valor: só o PRIMEIRO Bruno
remove("Zeca") -> false: [Carla, Bruno]             « não achou: false, e nada muda

Repare em três coisas: depois de remove(0) todos andaram uma casa para a esquerda (o Bruno virou o 0); remove("Bruno") tirou só o primeiro Bruno; e remover algo que não existe não é erro, só devolve false.

A pegadinha: remove numa lista de Integer

Numa lista de textos, remove(0) e remove("Bruno") não se confundem: um é número, o outro é texto. Mas e numa lista de números? A Ana guarda as notas de uma prova e quer tirar a nota 3, que foi lançada por engano:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> notas = new ArrayList<>();
        notas.add(10);
        notas.add(7);
        notas.add(3);
        notas.add(5);
        System.out.println("Notas: " + notas);

        notas.remove(3);    // a Ana quer tirar a nota 3...
        System.out.println("Depois de remove(3): " + notas);
    }
}
$ javac Main.java
$ java Main
Notas: [10, 7, 3, 5]
Depois de remove(3): [10, 7, 3]          « saiu o 5! O 3 continua lá

Nenhum erro, nenhum aviso, e o resultado está errado. O que aconteceu: 3, escrito assim, é um int. Entre as duas versões do remove, o Java escolhe a que encaixa sem conversão: remove(int posicao). Então ele removeu o elemento da posição 3, que é o 5.

Para dizer "remova o valor 3", o argumento precisa ser um objeto Integer, não um int. Há duas formas:

        notas.remove(Integer.valueOf(3));   // forma 1: cria um Integer
        notas.remove((Integer) 7);          // forma 2: conversão (cast) para Integer
        notas.remove(10);                   // isto é a POSIÇÃO 10...
$ javac Main.java
$ java Main
remove(Integer.valueOf(3)): [10, 7, 5]
remove((Integer) 7): [10, 5]
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 10 out of bounds for length 2
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
	at java.base/java.util.Objects.checkIndex(Objects.java:365)
	at java.base/java.util.ArrayList.remove(ArrayList.java:552)
	at Main.main(Main.java:18)

As duas primeiras formas removeram o valor certo. A terceira mostra o outro lado da pegadinha: a Ana queria tirar a nota 10, mas remove(10) pediu a posição 10 de uma lista com 2 elementos, e o programa parou com IndexOutOfBoundsException (as linhas at java.base/… são o caminho dentro da biblioteca do Java; a que importa é a última, Main.java:18, a sua linha).

Regra para listas de números

Em List<Integer>, remove(numero) é sempre por posição. Para remover por valor, use remove(Integer.valueOf(numero)). Mais sobre int × Integer em Wrappers e Autoboxing.

Missão: rode e veja que a nota errada sai. Corrija a linha do remove para que saia a nota 3 (o valor) e a saída termine com Depois: [10, 7, 5].

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> notas = new ArrayList<>();
        notas.add(10);
        notas.add(7);
        notas.add(3);
        notas.add(5);
        System.out.println("Notas: " + notas);

        notas.remove(3);
        System.out.println("Depois: " + notas);
    }
}
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> notas = new ArrayList<>();
        notas.add(10);
        notas.add(7);
        notas.add(3);
        notas.add(5);
        System.out.println("Notas: " + notas);

        notas.remove(Integer.valueOf(3));
        System.out.println("Depois: " + notas);
    }
}

Procurar: contains, indexOf, isEmpty

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> frutas = new ArrayList<>();
        frutas.add("maçã");
        frutas.add("banana");
        frutas.add("uva");
        frutas.add("banana");

        System.out.println("contains(\"uva\"): " + frutas.contains("uva"));
        System.out.println("contains(\"kiwi\"): " + frutas.contains("kiwi"));
        System.out.println("contains(\"Uva\"): " + frutas.contains("Uva"));
        System.out.println("indexOf(\"banana\"): " + frutas.indexOf("banana"));
        System.out.println("lastIndexOf(\"banana\"): " + frutas.lastIndexOf("banana"));
        System.out.println("indexOf(\"kiwi\"): " + frutas.indexOf("kiwi"));
        System.out.println("isEmpty(): " + frutas.isEmpty());
        frutas.clear();
        System.out.println("depois de clear(): " + frutas + " isEmpty(): " + frutas.isEmpty());
    }
}
$ javac Main.java
$ java Main
contains("uva"): true
contains("kiwi"): false
contains("Uva"): false              « maiúscula conta: "Uva" não é "uva"
indexOf("banana"): 1                « a posição da PRIMEIRA banana
lastIndexOf("banana"): 3            « a posição da ÚLTIMA
indexOf("kiwi"): -1                 « -1 = "não está na lista"
isEmpty(): false
depois de clear(): [] isEmpty(): true
MétodoPerguntaResposta
contains(x)"x está na lista?"true/false
indexOf(x)"Em que posição está o primeiro x?"A posição, ou -1
lastIndexOf(x)"E o último?"A posição, ou -1
isEmpty()"Está vazia?" (o mesmo que size() == 0)true/false
clear()Apaga tudo.nada (void)

Para comparar, esses métodos usam o equals de cada elemento, o mesmo que você usa para comparar Strings ("uva".equals("Uva") é false). Com objetos criados por você, como uma classe Aluno, contains só funciona direito se a classe tiver equals; veja A Classe Object.


Percorrer a lista

Há três jeitos comuns de passar por todos os elementos:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> alunos = new ArrayList<>();
        alunos.add("Ana");
        alunos.add("Bruno");
        alunos.add("Carla");

        // 1. for com índice
        for (int i = 0; i < alunos.size(); i++) {
            System.out.println((i + 1) + "º " + alunos.get(i));
        }

        // 2. for-each
        for (String aluno : alunos) {
            System.out.println("Olá, " + aluno + "!");
        }

        // 3. forEach com lambda
        alunos.forEach(aluno -> System.out.println("Tchau, " + aluno));
    }
}
$ javac Main.java
$ java Main
1º Ana
2º Bruno
3º Carla
Olá, Ana!
Olá, Bruno!
Olá, Carla!
Tchau, Ana
Tchau, Bruno
Tchau, Carla
FormaQuando usar
for (int i = 0; i < lista.size(); i++)Quando você precisa da posição (numerar, comparar com o vizinho, mexer só nos pares…). Repare: size(), não length.
for (String aluno : lista)O mais comum: "para cada aluno da lista". Mais curto e sem risco de errar o índice. Veja for-each.
lista.forEach(aluno -> …)Uma linha só, no estilo funcional. A setinha -> é uma lambda, assunto do próximo grupo de aulas.
Não remova dentro do for-each

Tirar elementos da lista enquanto um for-each passa por ela termina em ConcurrentModificationException. A aula Iterator mostra o erro real e as soluções (Iterator.remove e removeIf).


Ordenar

Há duas formas equivalentes de pôr a lista em ordem: o método estático Collections.sort(lista) (da classe Collections) e o método da própria lista, lista.sort(…).

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> alunos = new ArrayList<>();
        alunos.add("Carla");
        alunos.add("ana");
        alunos.add("Bruno");
        alunos.add("Álvaro");
        alunos.add("Ana");

        Collections.sort(alunos);
        System.out.println("Collections.sort: " + alunos);

        alunos.sort(Comparator.reverseOrder());
        System.out.println("ordem inversa:    " + alunos);

        alunos.sort(String.CASE_INSENSITIVE_ORDER);
        System.out.println("ignorando caixa:  " + alunos);

        List<Integer> notas = new ArrayList<>(List.of(7, 10, 3, 8));
        notas.sort(null);
        System.out.println("notas.sort(null): " + notas);
    }
}
$ javac Main.java
$ java Main
Collections.sort: [Ana, Bruno, Carla, ana, Álvaro]      « maiúsculas antes, acentos no fim
ordem inversa:    [Álvaro, ana, Carla, Bruno, Ana]
ignorando caixa:  [ana, Ana, Bruno, Carla, Álvaro]
notas.sort(null): [3, 7, 8, 10]
Collections.sort(alunos)
Ordena a própria lista (não cria outra) em ordem natural: números do menor para o maior, textos pela tabela de caracteres.
alunos.sort(Comparator.reverseOrder())
sort da lista recebe um comparador, um objeto que diz como comparar. reverseOrder() = ordem natural ao contrário.
String.CASE_INSENSITIVE_ORDER
Um comparador pronto que ignora maiúsculas/minúsculas.
notas.sort(null)
null no lugar do comparador quer dizer "use a ordem natural". É o mesmo que Collections.sort(notas).

Por que ana ficou depois de Carla, e Álvaro por último? A ordem natural das Strings compara os códigos de cada caractere (veja char e boolean): todas as maiúsculas sem acento (A=65 … Z=90) vêm antes das minúsculas (a=97 … z=122), e as letras acentuadas (Á=193) vêm depois de todas elas. Para ordenar como num dicionário, ou por qualquer critério seu (nota, idade, tamanho do nome), você vai usar comparadores na aula Comparable e Comparator.

Brinque com o código: acrescente nomes, troque a ordem do sort, experimente Collections.reverse(nomes) e nomes.getFirst().

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> nomes = new ArrayList<>();
        nomes.add("Duda");
        nomes.add("Bruno");
        nomes.add("Ana");
        nomes.add(0, "Carla");
        System.out.println("Lista: " + nomes);

        Collections.sort(nomes);
        System.out.println("Ordenada: " + nomes);
        System.out.println("Primeiro: " + nomes.get(0));
        System.out.println("Último: " + nomes.get(nomes.size() - 1));
    }
}

Listas prontas e imutáveis: List.of

Quando você já sabe os elementos, List.of(…) cria a lista numa linha só. Mas ela é imutável: não aceita add, remove, set nem sort.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> dias = List.of("seg", "ter", "qua", "qui", "sex");
        System.out.println(dias + " tamanho " + dias.size());
        System.out.println("get(2): " + dias.get(2));
        dias.add("sáb");
    }
}
$ javac Main.java
$ java Main
[seg, ter, qua, qui, sex] tamanho 5
get(2): qua
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:159)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:164)
	at Main.main(Main.java:8)

Ler funciona; mudar dá UnsupportedOperationException ("operação não suportada"). Repare que o erro aparece só na hora de rodar: o compilador aceitou o add, porque o tipo da variável é List, e toda List tem add.

Isso é útil: uma lista que não pode ser alterada por engano. Quando você quer alterar, crie uma ArrayList a partir dela. O construtor new ArrayList<>(outraColecao) copia todos os elementos:

        List<String> fixa = List.of("seg", "ter", "qua");
        List<String> dias = new ArrayList<>(fixa);
        dias.add("qui");
        System.out.println("fixa: " + fixa);
        System.out.println("dias: " + dias);
$ java Main
fixa: [seg, ter, qua]
dias: [seg, ter, qua, qui]        « a cópia mudou; a original continua igual

Formas de criar uma lista

FormaPode mudar?Quando usar
new ArrayList<>() + addSimA lista vai sendo preenchida durante o programa (lida do teclado, de um arquivo…).
List.of("a", "b")NãoValores fixos, conhecidos ao escrever o código.
new ArrayList<>(List.of("a", "b"))SimComeçar com alguns valores e depois mudar.
new ArrayList<>(outraLista)SimFazer uma cópia para mexer sem estragar a original.

Quando dá errado

IndexOutOfBoundsException: posição que não existe

Este código tem um erro de propósito.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> alunos = new ArrayList<>();
        alunos.add("Ana");
        alunos.add("Bruno");
        alunos.add("Carla");
        System.out.println("Último: " + alunos.get(alunos.size() - 1));
        System.out.println(alunos.get(3));
    }
}
$ javac Main.java
$ java Main
Último: Carla
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
	at java.base/java.util.Objects.checkIndex(Objects.java:365)
	at java.base/java.util.ArrayList.get(ArrayList.java:428)
	at Main.main(Main.java:11)

O que quer dizer: "índice 3 fora dos limites para tamanho 3". Com 3 elementos, as posições válidas são 0, 1 e 2. O nome é parecido com o do array (ArrayIndexOutOfBoundsException), mas aqui é IndexOutOfBoundsException. A pilha (stack trace) mostra o caminho pela biblioteca; procure a primeira linha com o seu arquivo: Main.java:11.

Atenção à lista vazia: get(0) numa lista sem nada dá Index 0 out of bounds for length 0. Antes de ler, confira com isEmpty().

Como consertar: use posições de 0 a size() - 1, e laços com i < lista.size() (nunca <=).

UnsupportedOperationException: mexeu numa lista imutável
$ java Main
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:159)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:164)
	at Main.main(Main.java:8)

O que quer dizer: a lista veio de List.of (ou de outro lugar que devolve listas imutáveis, como Collections.unmodifiableList e stream().toList()). A mensagem não tem texto: só o nome da exceção. A segunda linha diz qual operação foi recusada: .add.

Como consertar: faça uma cópia mutável: List<String> dias = new ArrayList<>(List.of(…));.

O remove tirou o número errado (e não deu erro)

O que aconteceu: numa List<Integer>, remove(3) remove a posição 3, não o valor 3. Veja a pegadinha.

Como consertar: remove(Integer.valueOf(3)).


Referência

MétodoO que fazExemplo → resultado (lista [a, b, c])
add(x)Acrescenta no fimadd("d") → [a, b, c, d]
add(i, x)Encaixa na posição iadd(0, "z") → [z, a, b, c]
get(i)Lê a posição iget(1) → b
set(i, x)Troca a posição i; devolve o antigoset(1, "B") → [a, B, c], devolve b
remove(i)Remove a posição i; devolve o removidoremove(0) → [b, c], devolve a
remove(x)Remove a 1ª ocorrência de xremove("c") → [a, b], devolve true
size()Quantidade3
isEmpty()Está vazia?false
contains(x)Contém x?contains("b") → true
indexOf(x) / lastIndexOf(x)Posição da 1ª / última ocorrênciaindexOf("z") → -1
clear()Apaga tudo[]
sort(null) / Collections.sort(l)Ordem natural—
sort(Comparator.reverseOrder())Ordem inversa[c, b, a]
getFirst() / getLast() Java 21+Primeiro / últimoa / c
forEach(x -> …)Faz algo com cada um—
List.of(…)Lista pronta e imutável—
new ArrayList<>(col)Cópia mutável de outra coleção—

Exercícios

1. O primeiro

Complete para ler o primeiro elemento da lista alunos:

String primeiro = alunos.;

2. Trocar sem aumentar

A posição 2 tem "feijão". Complete para trocar por "lentilha", sem mudar o tamanho da lista:

compras.;

3. Remover o valor

Na List<Integer> notas, complete para remover o valor 8 (e não a posição 8):

notas.remove();

4. A lista é [10, 20, 30] (de Integer). Como ela fica depois de remove(1)?

5. O que frutas.indexOf("kiwi") devolve se não houver kiwi?

6. List.of("a", "b").add("c")…

Missão final: o programa quebra porque a lista veio de List.of. Faça uma cópia mutável com new ArrayList<>(…); depois coloque "leite" no começo, remova "feijão", ordene e mostre a lista seguida do tamanho. A saída deve ser [arroz, café, leite] - 3 itens.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> compras = List.of("arroz", "feijão", "café");
        compras.add(0, "leite");
        System.out.println(compras + " - " + compras.size() + " itens");
    }
}
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> compras = new ArrayList<>(List.of("arroz", "feijão", "café"));
        compras.add(0, "leite");
        compras.remove("feijão");
        compras.sort(null);
        System.out.println(compras + " - " + compras.size() + " itens");
    }
}

Resumo

  • List<String> l = new ArrayList<>(); cria uma lista vazia que cresce sozinha. Posições começam em 0.
  • add(x) põe no fim; add(i, x) encaixa; set(i, x) substitui; get(i) lê; size() conta.
  • remove(i) remove por posição; remove(objeto) remove por valor e devolve true/false.
  • Em List<Integer>, remove(3) é a posição 3; para o valor, remove(Integer.valueOf(3)).
  • contains, indexOf (-1 = não achou) e isEmpty procuram; comparam com equals.
  • Percorra com for-each (o mais comum), com índice (quando precisa da posição) ou com forEach.
  • Ordene com Collections.sort(l) ou l.sort(null); a ordem natural das Strings põe maiúsculas antes e acentos no fim.
  • Posição inválida: IndexOutOfBoundsException. Mudar uma List.of: UnsupportedOperationException.

Próxima aula: LinkedList, a outra lista do Java, e quando (quase nunca) preferi-la.