Comparação entre arrays tradicionais e ArrayList, tipos de referência e autoboxing
O ArrayList é uma estrutura de dados dinâmica do Java que faz parte do pacote java.util. Diferentemente dos arrays tradicionais ([]), o ArrayList pode crescer e diminuir de tamanho conforme necessário.
Diferença principal:
for-each percorrendo o array
de Strings nomes ({"Ana", "Bruno",
"Carlos", "Diana", "Eduardo"}). A cada iteração,
nome recebe um dos valores do array — o
tamanho fixo do array define exatamente quantas vezes
o laço executa.
public class ExemploArray {
public static void main(String[] args) {
// Tamanho fixo: 5 elementos
String[] nomes = new String[5];
// Adicionando elementos manualmente
nomes[0] = "Ana";
nomes[1] = "Bruno";
nomes[2] = "Carlos";
nomes[3] = "Diana";
nomes[4] = "Eduardo";
// Tamanho fixo - não pode adicionar mais elementos
System.out.println("Tamanho do array: " + nomes.length);
// Percorrendo o array
for (String nome : nomes) {
System.out.println(nome);
}
}
}
Tamanho do array: 5
Ana
Bruno
Carlos
Diana
Eduardo
import java.util.ArrayList;
public class ExemploArrayList {
public static void main(String[] args) {
// Criando ArrayList sem definir tamanho
ArrayList<String> nomes = new ArrayList<>();
// Adicionando elementos dinamicamente
nomes.add("Ana");
nomes.add("Bruno");
nomes.add("Carlos");
nomes.add("Diana");
nomes.add("Eduardo");
// Pode adicionar mais elementos a qualquer momento
nomes.add("Fernanda");
nomes.add("Gabriel");
System.out.println("Tamanho do ArrayList: " + nomes.size());
// Percorrendo o ArrayList
for (String nome : nomes) {
System.out.println(nome);
}
}
}
Tamanho do ArrayList: 7
Ana
Bruno
Carlos
Diana
Eduardo
Fernanda
Gabriel
| Característica | Array [] | ArrayList |
|---|---|---|
| Tamanho | Fixo | Dinâmico |
| Declaração | int[] nums = new int[5]; |
ArrayList<Integer> nums = new ArrayList<>(); |
| Adicionar elemento | nums[0] = 10; |
nums.add(10); |
| Acessar elemento | nums[0] |
nums.get(0) |
| Tamanho | nums.length |
nums.size() |
| Remover elemento | Não suportado | nums.remove(0) |
| Tipos primitivos | Suporta diretamente | Requer wrapper (Integer, Double, etc.) |
<>?Sim, o ArrayList sempre precisa que você especifique o tipo entre <> — mas com uma ressalva importante: ArrayList só aceita tipos de referência (objetos), não tipos primitivos.
✔ Declarações corretas:
ArrayList<String> — corretoArrayList<Integer> — correto (wrapper de int)ArrayList<Double> — correto (wrapper de double)ArrayList<Character> — correto (wrapper de char)ArrayList<Boolean> — correto (wrapper de boolean)✘ Declaração incorreta — erro de compilação:
ArrayList<int> — erro! int é primitivoArrayList<double> — erro! double é primitivoArrayList<char> — erro! char é primitivo| Tipo Primitivo | Classe Wrapper (use no ArrayList) | Exemplo de declaração |
|---|---|---|
int |
Integer |
ArrayList<Integer> nums = new ArrayList<>(); |
double |
Double |
ArrayList<Double> precos = new ArrayList<>(); |
char |
Character |
ArrayList<Character> letras = new ArrayList<>(); |
boolean |
Boolean |
ArrayList<Boolean> flags = new ArrayList<>(); |
float |
Float |
ArrayList<Float> valores = new ArrayList<>(); |
long |
Long |
ArrayList<Long> ids = new ArrayList<>(); |
O Java faz a conversão automática entre o tipo primitivo e seu wrapper. Isso se chama autoboxing (primitivo → objeto) e unboxing (objeto → primitivo). Na prática, você escreve int mas o ArrayList recebe Integer automaticamente.
import java.util.ArrayList;
public class AutoboxingDemo {
public static void main(String[] args) {
ArrayList<Integer> numeros = new ArrayList<>();
// autoboxing: int 42 é convertido para Integer automaticamente
numeros.add(42);
numeros.add(100);
numeros.add(7);
// unboxing: Integer é convertido de volta para int automaticamente
int primeiro = numeros.get(0);
System.out.println("Lista: " + numeros);
System.out.println("Primeiro elemento: " + primeiro);
System.out.println("Soma dos dois primeiros: " + (numeros.get(0) + numeros.get(1)));
}
}
Lista: [42, 100, 7]
Primeiro elemento: 42
Soma dos dois primeiros: 142
💡 Dica: Graças ao autoboxing, você raramente precisa se preocupar com a conversão entre int e Integer — o Java cuida disso nos bastidores. Mas lembre-se: a declaração do ArrayList ainda exige o wrapper (Integer, não int).
<>O <> vazio (chamado de diamond operator) só é válido no lado direito da atribuição, onde o compilador consegue inferir o tipo pelo lado esquerdo.
✔ Correto: tipo declarado na esquerda, <> inferido na direita
// O compilador lê "ArrayList<String>" na esquerda e infere o tipo na direita
ArrayList<String> lista = new ArrayList<>();
✘ Erro de compilação: <> na esquerda — o compilador não tem como inferir o tipo
// ERRO: compilador não sabe qual tipo usar no lado esquerdo ArrayList<> lista = new ArrayList<String>(); // não compila!
⚠ Atenção — Raw Type (sem nenhum tipo): Tecnicamente é possível usar ArrayList sem <> (chamado de raw type), mas é má prática pois perde a segurança de tipos em tempo de compilação e pode gerar alertas de unchecked warning.
// Má prática — raw type, evite! ArrayList lista = new ArrayList(); lista.add("texto"); lista.add(42); // aceita qualquer coisa — sem segurança de tipos
Resumo da regra:
ArrayList<String> lista = new ArrayList<>(); — ✔ tipo na esquerda, inferido na direitaArrayList<String> lista = new ArrayList<String>(); — ✔ tipo em ambos os lados (redundante mas válido)ArrayList<> lista = new ArrayList<String>(); — ✘ erro de compilaçãoArrayList lista = new ArrayList(); — ✔ compila, mas é má prática (raw type)ArrayList pode armazenar diferentes tipos de objetos (um ArrayList por tipo):
import java.util.ArrayList;
public class ArrayListTipos {
public static void main(String[] args) {
// ArrayList de Strings
ArrayList<String> frutas = new ArrayList<>();
frutas.add("Maçã");
frutas.add("Banana");
frutas.add("Laranja");
// ArrayList de Integer (wrapper de int — não pode usar int diretamente)
ArrayList<Integer> numeros = new ArrayList<>();
numeros.add(10); // autoboxing: int → Integer
numeros.add(20);
numeros.add(30);
// ArrayList de Double (wrapper de double)
ArrayList<Double> precos = new ArrayList<>();
precos.add(19.99); // autoboxing: double → Double
precos.add(29.50);
precos.add(15.75);
// ArrayList de Boolean (wrapper de boolean)
ArrayList<Boolean> status = new ArrayList<>();
status.add(true);
status.add(false);
status.add(true);
System.out.println("Frutas: " + frutas);
System.out.println("Números: " + numeros);
System.out.println("Preços: " + precos);
System.out.println("Status: " + status);
}
}
Frutas: [Maçã, Banana, Laranja]
Números: [10, 20, 30]
Preços: [19.99, 29.5, 15.75]
Status: [true, false, true]
Por que 29.5 e não 29.50? O método toString() do Java remove zeros à direita em números decimais. Para formatar com duas casas decimais, use String.format("%.2f", valor).
Existem diferentes formas de percorrer um ArrayList:
import java.util.ArrayList;
public class PercorrerArrayList {
public static void main(String[] args) {
ArrayList<String> cidades = new ArrayList<>();
cidades.add("São Paulo");
cidades.add("Rio de Janeiro");
cidades.add("Belo Horizonte");
cidades.add("Salvador");
// Forma 1: For tradicional com índice
System.out.println("Forma 1: For tradicional");
for (int i = 0; i < cidades.size(); i++) {
System.out.println(i + ": " + cidades.get(i));
}
// Forma 2: For-each
System.out.println("Forma 2: For-each");
for (String cidade : cidades) {
System.out.println("- " + cidade);
}
// Forma 3: forEach com lambda (Java 8+)
System.out.println("Forma 3: forEach lambda");
cidades.forEach(cidade -> System.out.println("* " + cidade));
}
}
Forma 1: For tradicional
0: São Paulo
1: Rio de Janeiro
2: Belo Horizonte
3: Salvador
Forma 2: For-each
- São Paulo
- Rio de Janeiro
- Belo Horizonte
- Salvador
Forma 3: forEach lambda
* São Paulo
* Rio de Janeiro
* Belo Horizonte
* Salvador
Vamos explorar as operações mais comuns:
import java.util.ArrayList;
public class OperacoesArrayList {
public static void main(String[] args) {
ArrayList<String> linguagens = new ArrayList<>();
// 1. Adicionar elementos
linguagens.add("Java");
linguagens.add("Python");
linguagens.add("JavaScript");
System.out.println("Após adicionar: " + linguagens);
// 2. Adicionar em posição específica
linguagens.add(1, "C++");
System.out.println("Após add(1, C++): " + linguagens);
// 3. Acessar elemento
String primeira = linguagens.get(0);
System.out.println("Primeira linguagem: " + primeira);
// 4. Modificar elemento
linguagens.set(2, "Ruby");
System.out.println("Após modificar índice 2: " + linguagens);
// 5. Remover elemento pelo valor
linguagens.remove("Ruby");
System.out.println("Após remover Ruby: " + linguagens);
// 6. Remover por índice
linguagens.remove(0);
System.out.println("Após remover índice 0: " + linguagens);
// 7. Verificar se contém
boolean temPython = linguagens.contains("Python");
System.out.println("Contém Python? " + temPython);
// 8. Tamanho
System.out.println("Tamanho: " + linguagens.size());
// 9. Verificar se está vazio
System.out.println("Está vazio? " + linguagens.isEmpty());
}
}
Após adicionar: [Java, Python, JavaScript]
Após add(1, C++): [Java, C++, Python, JavaScript]
Primeira linguagem: Java
Após modificar índice 2: [Java, C++, Ruby, JavaScript]
Após remover Ruby: [Java, C++, JavaScript]
Após remover índice 0: [C++, JavaScript]
Contém Python? false
Tamanho: 2
Está vazio? false
Use Array [] quando:
int, double, etc.) sem overhead de wrapperUse ArrayList quando:
add, remove, contains, etc.)Teste seus conhecimentos sobre ArrayList versus Array, tipos wrapper e o diamond operator em Java:
Reforce o conteúdo desta aula com mais dois exercícios práticos: