Dominando o ArrayList em Java

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, ArrayList ajusta 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: ArrayList só pode armazenar referências a objetos. Para tipos primitivos (como int, 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: ArrayList não é seguro para uso concorrente em ambientes multithread. Para cenários que exigem segurança de thread, considere usar Vector ou Collections.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. Retorna true.

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. Retorna true se o elemento foi encontrado e removido.
  • E remove(int index): Remove o elemento no índice especificado e retorna o elemento removido. Lança IndexOutOfBoundsException se 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ça IndexOutOfBoundsException se 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ça IndexOutOfBoundsException se 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 (usa equals()).
  • 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 de Object.
  • <T> T[] toArray(T[] a): Converte a lista em um array do tipo especificado.
  • boolean isEmpty(): Retorna true se 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:

  1. 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.
  2. Loop for tradicional com índice (for (int i = 0; i < list.size(); i++)): Útil quando o índice é necessário.
  3. 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)
   

Tags: java ArrayList Coleções generics estrutura de dados

Publicado em 6-18 10:09