O ArrayList, parte do pacote java.util, é uma implementação de lista dinâmica em Java, baseada em um array redimensionável. Ele oferece flexibilidade em comparação com arrays tradicionais, que possuem tamanho fixo.
Características Principais do ArrayList
- Tamanho Dinâmico: Diferente de arrays nativos,
ArrayListajusta seu tamanho automaticamente conforme elementos são adicionados ou removidos. A capacidade interna cresce (geralmente em 1.5 vezes o tamanho anterior) quando o limite é atingido, e pode encolher. - Armazenamento de Referências:
ArrayListsó pode armazenar referências a objetos. Para tipos primitivos (comoint,double), é necessário usar suas classes wrapper correspondantes (Integer,Double, etc.). - Acesso Aleatório Eficiente: Devido à sua implementação baseada em array, acessar um elemento por seu índice (
get(index)) é uma operação de tempo constante, O(1). - Inserção/Remoção Lenta no Meio: Adicionar ou remover elementos no início ou no meio da lista requer o deslocamento de outros elementos, rseultando em uma complexidade de tempo linear, O(n). Adicionar no final é geralmente O(1), amortizado.
- Não é Thread-Safe:
ArrayListnão é seguro para uso concorrente em ambientes multithread. Para cenários que exigem segurança de thread, considere usarVectorouCollections.synchronizedList().
Classes Wrapper e Generics
Para utilizar tipos primitivos em coleções como ArrayList, usamos as classes wrapper:
| Tipo Primitivo | Classe Wrapper |
|---|---|
byte |
Byte |
short |
Short |
char |
Character |
int |
Integer |
long |
Long |
float |
Float |
double |
Double |
boolean |
Boolean |
O Java 5 introduziu o Autoboxing (conversão automática de tipo primitivo para wrapper) e Unboxing (conversão de wrapper para primitivo), simplificando o uso:
// Antes (JDK 5)
Integer countObj = new Integer(100);
// Com Autoboxing (JDK 5+)
Integer countObjAuto = 100;
// Com Unboxing
int countPrim = countObjAuto;
Generics (<Tipo>) garantem a segurança de tipo em tempo de compilação, prevenindo erros de ClassCastException em tempo de execução e eliminando a necessidade de type casting manual.
// Cria um ArrayList que só aceita Strings
ArrayList<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// names.add(123); // Erro de compilação! Não é possível adicionar um Integer.
// Combinando Generics com Wrappers
ArrayList<Integer> scores = new ArrayList<>();
scores.add(90); // Autoboxing para Integer
int highestScore = scores.get(0); // Unboxing para int
Comparativo: Array vs. ArrayList
| Característica | Array | ArrayList |
|---|---|---|
| Tamanho | Fixo | Dinâmico (redimensionável) |
| Tipos Armazenados | Primitivos e Objetos | Apenas Objetos (requer wrappers para primitivos) |
| Generics | Não suportado | Suportado |
| Redimensionamento Automático | Não | Sim |
| Acesso por Índice | O(1) | O(1) |
| Inserção/Remoção (meio) | O(n) | O(n) |
Métodos Comuns do ArrayList
Adição (Add)
boolean add(E element): Adiciona o elemento ao final da lista. Retornatrue.
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
System.out.println(fruits); // Saída: [Apple, Banana]
Remoção (Remove)
boolean remove(Object o): Remove a primeira ocorrência do elemento especificado. Retornatruese o elemento foi encontrado e removido.E remove(int index): Remove o elemento no índice especificado e retorna o elemento removido. LançaIndexOutOfBoundsExceptionse o índice for inválido.
ArrayList<String> colors = new ArrayList<>();
colors.add("Red");
colors.add("Green");
colors.add("Blue");
boolean removedGreen = colors.remove("Green"); // true
String removedFirst = colors.remove(0); // "Red"
System.out.println("Green removido? " + removedGreen); // Saída: Green removido? true
System.out.println("Primeiro elemento removido: " + removedFirst); // Saída: Primeiro elemento removido: Red
System.out.println(colors); // Saída: [Blue]
Modificação (Set)
E set(int index, E element): Substitui o elemento no índice especificado pelo novo elemento. Retorna o elemento original que estava naquele índice. LançaIndexOutOfBoundsExceptionse o índice for inválido.
ArrayList<String> cities = new ArrayList<>();
cities.add("London");
cities.add("Paris");
String oldCity = cities.set(0, "Tokyo"); // Substitui "London" por "Tokyo"
System.out.println(cities); // Saída: [Tokyo, Paris]
System.out.println("Cidade antiga: " + oldCity); // Saída: Cidade antiga: London
Consulta (Get & Size)
E get(int index): Retorna o elemento no índice especificado. LançaIndexOutOfBoundsExceptionse o índice for inválido.int size(): Retorna o número de elementos na lista.
ArrayList<String> animals = new ArrayList<>();
animals.add("Dog");
animals.add("Cat");
System.out.println("Número de animais: " + animals.size()); // Saída: Número de animais: 2
System.out.println("Segundo animal: " + animals.get(1)); // Saída: Segundo animal: Cat
Outros Métodos Úteis
boolean contains(Object o): Verifica se a lista contém o elemento especificado (usaequals()).int indexOf(Object o): Retorna o índice da primeira ocorrência do elemento, ou -1 se não for encontrado.int lastIndexOf(Object o): Retorna o índice da última ocorrência do elemento, ou -1 se não for encontrado.void clear(): Remove todos os elementos da lista.Object[] toArray(): Converte a lista em um array deObject.<T> T[] toArray(T[] a): Converte a lista em um array do tipo especificado.boolean isEmpty(): Retornatruese a lista não contiver elementos.
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(10);
System.out.println("Contém 20? " + numbers.contains(20)); // Saída: Contém 20? true
System.out.println("Índice de 10: " + numbers.indexOf(10)); // Saída: Índice de 10: 0
System.out.println("Último índice de 10: " + numbers.lastIndexOf(10)); // Saída: Último índice de 10: 2
numbers.clear();
System.out.println("Está vazia? " + numbers.isEmpty()); // Saída: Está vazia? true
// Conversão para array
ArrayList<String> items = new ArrayList<>();
items.add("Item A");
String[] itemArray = items.toArray(new String[0]); // Usa um array de tamanho 0 como template
System.out.println("Primeiro item do array: " + itemArray[0]); // Saída: Primeiro item do array: Item A
Exemplos de Operações Comuns
Iterando sobre um ArrayList
Existem várias maneiras de percorrer os elementos:
- For-each loop (
for (Tipo elemento : colecao)): Preferível pela clareza e concisão. Não permite modificação direta da lista durante a iteração. - Loop for tradicional com índice (
for (int i = 0; i < list.size(); i++)): Útil quando o índice é necessário. - Iterator: Permite a remoção segura de elementos durante a iteração usando
iterator.remove().
import java.util.ArrayList;
import java.util.Iterator;
public class ListIteration {
public static void main(String[] args) {
ArrayList<Integer> dataPoints = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
dataPoints.add(i * 10); // Adiciona 10, 20, 30, 40, 50
}
System.out.println("Usando for-each:");
for (int point : dataPoints) {
System.out.print(point + " ");
}
System.out.println("\n");
System.out.println("Usando for tradicional com índice:");
for (int i = 0; i < dataPoints.size(); i++) {
System.out.print(dataPoints.get(i) + " ");
}
System.out.println("\n");
System.out.println("Usando Iterator (com remoção de múltiplos de 30):");
Iterator<Integer> iterator = dataPoints.iterator();
while (iterator.hasNext()) {
Integer currentPoint = iterator.next();
System.out.print(currentPoint + " ");
if (currentPoint % 30 == 0) {
iterator.remove(); // Remoção segura
}
}
System.out.println("\nLista após remoção: " + dataPoints);
}
}
Saída esperada:
Usando for-each:
10 20 30 40 50
Usando for tradicional com índice:
10 20 30 40 50
Usando Iterator (com remoção de múltiplos de 30):
10 20 30 40 50
Lista após remoção: [10, 20, 40, 50]
Adicionar Objetos Personalizados e Verificar Existência
Ao armazenar objetos de classes personalizadas, é crucial sobrescrever os métodos equals() e hashCode() para que métodos como contains() e remove(Object) funcionem corretamente, comparando o conteúdo em vez das referências de memória.
import java.util.ArrayList;
import java.util.Objects;
class Product {
private String sku;
private String name;
private double price;
public Product(String sku, String name, double price) {
this.sku = sku;
this.name = name;
this.price = price;
}
public String getSku() { return sku; }
public String getName() { return name; }
public double getPrice() { return price; }
// Sobrescrevendo equals e hashCode para comparação baseada no SKU
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return Objects.equals(sku, product.sku);
}
@Override
public int hashCode() {
return Objects.hash(sku);
}
@Override
public String toString() {
return "Product{sku='" + sku + "', name='" + name + "', price=" + price + '}';
}
}
public class ManageProducts {
public static void main(String[] args) {
ArrayList<Product> inventory = new ArrayList<>();
Product p1 = new Product("P001", "Laptop", 1200.00);
Product p2 = new Product("P002", "Mouse", 25.50);
Product p3 = new Product("P003", "Keyboard", 75.00);
inventory.add(p1);
inventory.add(p2);
// Verificar se um produto com SKU "P001" existe
Product searchProduct = new Product("P001", "Laptop", 0.0); // Preço não importa para equals
if (inventory.contains(searchProduct)) {
System.out.println("Produto com SKU P001 já está no inventário.");
} else {
System.out.println("Produto com SKU P001 não encontrado.");
}
// Adicionar o terceiro produto
inventory.add(p3);
System.out.println("Inventário atualizado: " + inventory);
}
}
Saída esperada:
Produto com SKU P001 já está no inventário.
Inventário atualizado: [Product{sku='P001', name='Laptop', price=1200.0}, Product{sku='P002', name='Mouse', price=25.5}, Product{sku='P003', name='Keyboard', price=75.0}]
Filtrar e Retornar uma Nova Lista
Um padrão comum é iterar sobre uma lista, aplicar um critério de filtragem e retornar uma nova lista contendo apenas os elementos que satisfazem a condição.
import java.util.ArrayList;
class Book {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() { return title; }
public double getPrice() { return price; }
@Override
public String toString() {
return "Book{title='" + title + "', price=" + price + '}';
}
}
public class FilterBooks {
public static void main(String[] args) {
ArrayList<Book> allBooks = new ArrayList<>();
allBooks.add(new Book("Java Basics", 45.00));
allBooks.add(new Book("Advanced Algorithms", 70.00));
allBooks.add(new Book("Data Structures", 55.00));
allBooks.add(new Book("Web Development", 65.00));
// Filtrar livros com preço inferior a 60.00
ArrayList<Book> affordableBooks = filterByPrice(allBooks, 60.00);
System.out.println("Livros acessíveis:");
for (Book book : affordableBooks) {
System.out.println("- " + book.getTitle() + " ($" + book.getPrice() + ")");
}
}
/**
* Filtra uma lista de livros, retornando uma nova lista com livros cujo preço é inferior a um limite.
* @param books A lista original de livros.
* @param priceLimit O preço máximo permitido.
* @return Uma nova lista contendo apenas os livros acessíveis.
*/
public static ArrayList<Book> filterByPrice(ArrayList<Book> books, double priceLimit) {
ArrayList<Book> filteredList = new ArrayList<>();
for (Book book : books) {
if (book.getPrice() < priceLimit) {
filteredList.add(book);
}
}
return filteredList;
}
}
Saída esperada:
Livros acessíveis:
- Java Basics ($45.0)
- Data Structures ($55.0)