JAVA Learning

Hub de Conteúdo

Estruturas de Dados em Java

Domine as estruturas fundamentais para organizar e manipular dados eficientemente

1. O que são Estruturas de Dados?

Estruturas de dados são formas de organizar e armazenar dados na memória do computador. Cada estrutura possui características únicas que a tornam ideal para diferentes situações.

Uma boa escolha de estrutura de dados pode tornar seu programa mais eficiente, rápido e fácil de manter. Usar a estrutura errada pode resultar em performance ruim e código complexo.

Em Java, as principais estruturas estão no pacote java.util, conhecidas como Collections Framework.

2. Arrays - Estrutura Fixa

Um array é uma coleção de elementos do mesmo tipo armazenados em posições contíguas da memória.

Características:

  • Tamanho fixo - definido na criação
  • Acesso rápido - O(1) por índice
  • Tipado - todos os elementos do mesmo tipo
  • Alocação contígua - em memória sequencial

Exemplos Práticos:

java
// Array de inteiros
int[] numeros = new int[5]; // Cria array com 5 posições
numeros[0] = 10;
numeros[1] = 20;
numeros[2] = 30;

// Array inicializado
int[] notas = {7, 8, 9, 10, 6};

// Percorrer array
for (int i = 0; i < notas.length; i++) {
    System.out.println("Nota: " + notas[i]);
}

// Array de Strings
String[] nomes = {"João", "Maria", "Pedro"};
System.out.println(nomes[0]); // João

// Array bidimensional (matriz)
int[][] matriz = new int[3][3];
matriz[0][0] = 1;
matriz[0][1] = 2;
matriz[0][2] = 3;

// Acessar matriz
System.out.println(matriz[0][1]); // 2

3. ArrayList - Dinâmico e Flexível

ArrayList é uma lista dinâmica que cresce automaticamente conforme você adiciona elementos.

Características:

  • Tamanho dinâmico - cresce automaticamente
  • Acesso rápido - O(1) por índice
  • Generics - tipagem de elementos
  • Métodos úteis - add(), remove(), get(), size()

Exemplo Completo:

java
import java.util.ArrayList;

// Criar ArrayList
ArrayList frutas = new ArrayList<>();

// Adicionar elementos
frutas.add("Maçã");
frutas.add("Banana");
frutas.add("Laranja");

System.out.println("Total de frutas: " + frutas.size()); // 3

// Acessar elemento
System.out.println(frutas.get(0)); // Maçã

// Adicionar em posição específica
frutas.add(1, "Morango");

// Remover elemento
frutas.remove("Banana");
frutas.remove(0); // Remove por índice

// Verificar se existe
if (frutas.contains("Laranja")) {
    System.out.println("Laranja encontrada!");
}

// Percorrer com foreach
for (String fruta : frutas) {
    System.out.println(fruta);
}

// Limpar lista
frutas.clear();
System.out.println("Lista vazia: " + frutas.isEmpty()); // true

ArrayList com Objetos:

java
// Classe Produto
public class Produto {
    private String nome;
    private double preco;
    
    public Produto(String nome, double preco) {
        this.nome = nome;
        this.preco = preco;
    }
    
    public String getNome() { return nome; }
    public double getPreco() { return preco; }
}

// Usar ArrayList com objetos
ArrayList carrinho = new ArrayList<>();
carrinho.add(new Produto("Notebook", 3000));
carrinho.add(new Produto("Mouse", 50));
carrinho.add(new Produto("Teclado", 150));

// Calcular total
double total = 0;
for (Produto p : carrinho) {
    total += p.getPreco();
}
System.out.println("Total do carrinho: R$ " + total);

4. Outras Estruturas de Lista

LinkedList - Acesso Sequencial

LinkedList usa nós encadeados, melhor para inserções/remoções no meio. Acesso sequencial é mais lento.

java
import java.util.LinkedList;

LinkedList numeros = new LinkedList<>();
numeros.add(1);
numeros.add(2);
numeros.add(3);

// Adicionar no início
numeros.addFirst(0);

// Adicionar no final
numeros.addLast(4);

// Remover primeiro
numeros.removeFirst();

// Remover último
numeros.removeLast();

// Obter primeiro
int primeiro = numeros.getFirst();

// Obter último
int ultimo = numeros.getLast();

5. Queue (Fila) - FIFO

Queue (fila) segue o princípio FIFO (First In, First Out) - o primeiro a entrar é o primeiro a sair.

Principais Métodos:

  • offer() ou add() - adiciona elemento
  • poll() ou remove() - remove primeiro
  • peek() ou element() - visualiza primeiro
  • size() - tamanho da fila

Exemplo Prático:

java
import java.util.Queue;
import java.util.LinkedList;

// Criar fila
Queue fila = new LinkedList<>();

// Adicionar elementos
fila.offer("João");
fila.offer("Maria");
fila.offer("Pedro");

System.out.println("Tamanho: " + fila.size()); // 3

// Ver primeiro sem remover
System.out.println("Próximo: " + fila.peek()); // João

// Remover primeiro
String primeiro = fila.poll();
System.out.println("Atendido: " + primeiro); // João

// Processar fila
while (!fila.isEmpty()) {
    String pessoa = fila.poll();
    System.out.println("Atendendo: " + pessoa);
}

Simulação: Fila de Banco

java
Queue filaBanco = new LinkedList<>();

// Clientes chegando
filaBanco.offer("Cliente A");
filaBanco.offer("Cliente B");
filaBanco.offer("Cliente C");

// Atendimento
int caixa = 1;
while (!filaBanco.isEmpty()) {
    String cliente = filaBanco.poll();
    System.out.println("Caixa " + caixa + " atende: " + cliente);
}

6. Stack (Pilha) - LIFO

Stack (pilha) segue o princípio LIFO (Last In, First Out) - o último a entrar é o primeiro a sair.

Principais Métodos:

  • push() - adiciona elemento no topo
  • pop() - remove do topo
  • peek() - visualiza topo
  • empty() - verifica se vazia

Exemplo Prático:

java
import java.util.Stack;

// Criar pilha
Stack pilha = new Stack<>();

// Adicionar elementos
pilha.push(1);
pilha.push(2);
pilha.push(3);
pilha.push(4);

System.out.println("Topo: " + pilha.peek()); // 4

// Remover todos
while (!pilha.empty()) {
    int valor = pilha.pop();
    System.out.println("Removido: " + valor);
}

// Ordem: 4, 3, 2, 1

Aplicação Real: Desfazer (Undo)

java
// Simular ações em um editor de texto
Stack historico = new Stack<>();

// Ações do usuário
historico.push("Digitou: Hello");
historico.push("Digitou: World");
historico.push("Apagou: World");

System.out.println("Histórico:");
while (!historico.empty()) {
    System.out.println("- " + historico.pop());
}

// Saída (em ordem reversa):
// - Apagou: World
// - Digitou: World
// - Digitou: Hello

7. Comparação de Estruturas

Estrutura Tipo Acesso Inserção Remoção Melhor Uso
Array Fixo O(1) O(n) O(n) Dados imutáveis
ArrayList Dinâmico O(1) O(n) O(n) Acesso frequente
LinkedList Dinâmico O(n) O(1)* O(1)* Inserções no meio
Queue FIFO O(1) O(1) O(1) Fila de espera
Stack LIFO O(1) O(1) O(1) Histórico, backtrack

* Com referência ao nó

8. Set e Map - Estruturas Especializadas

HashSet - Conjunto Único

HashSet armazena valores únicos sem duplicatas. Não mantém ordem.

java
import java.util.HashSet;
import java.util.Set;

Set cores = new HashSet<>();
cores.add("Vermelho");
cores.add("Azul");
cores.add("Verde");
cores.add("Vermelho"); // Não será adicionado (duplicata)

System.out.println("Total de cores: " + cores.size()); // 3

// Verificar se existe
if (cores.contains("Azul")) {
    System.out.println("Azul existe!");
}

// Remover
cores.remove("Verde");

// Percorrer
for (String cor : cores) {
    System.out.println(cor);
}

HashMap - Chave-Valor

HashMap armazena pares chave-valor. Ideal para buscas por chave.

java
import java.util.HashMap;
import java.util.Map;

// Criar mapa
Map notas = new HashMap<>();

// Adicionar pares
notas.put("João", 85);
notas.put("Maria", 92);
notas.put("Pedro", 78);

// Obter valor por chave
int nota = notas.get("João"); // 85

// Verificar se chave existe
if (notas.containsKey("Maria")) {
    System.out.println("Nota de Maria: " + notas.get("Maria"));
}

// Verificar se valor existe
if (notas.containsValue(78)) {
    System.out.println("Existe alguém com nota 78");
}

// Iterar sobre pares
for (String aluno : notas.keySet()) {
    System.out.println(aluno + ": " + notas.get(aluno));
}

// Remover
notas.remove("Pedro");

// Tamanho
System.out.println("Total de alunos: " + notas.size());

9. Boas Práticas com Estruturas de Dados

✓ FAÇA:

  • Escolha a estrutura certa para o problema
  • Use generics para segurança de tipo
  • Verifique tamanho antes de acessar
  • Use for-each quando possível
  • Considere performance e memória
  • Documente por que escolheu cada estrutura

✗ EVITE:

  • Usar ArrayList para tudo (nem sempre é a melhor)
  • Acessar índices sem verificar limites
  • Modificar coleções enquanto itera
  • Esquecer de verificar null
  • Usar estruturas inadequadas por preguiça

10. Exemplo Prático: Sistema de Gerenciamento

java
import java.util.*;

public class SistemaEstoque {
    private Map produtos;
    private Queue pedidos;
    private Stack historico;
    
    public SistemaEstoque() {
        this.produtos = new HashMap<>();
        this.pedidos = new LinkedList<>();
        this.historico = new Stack<>();
    }
    
    public void adicionarProduto(int id, String nome, double preco) {
        produtos.put(id, new Produto(id, nome, preco));
        historico.push("Produto adicionado: " + nome);
    }
    
    public void fazerPedido(String cliente, int idProduto) {
        if (produtos.containsKey(idProduto)) {
            Produto p = produtos.get(idProduto);
            pedidos.offer(new Pedido(cliente, p));
            historico.push("Pedido de " + cliente + " para " + p.getNome());
        }
    }
    
    public void processarPedidos() {
        while (!pedidos.isEmpty()) {
            Pedido p = pedidos.poll();
            System.out.println("Processando: " + p);
        }
    }
    
    public void exibirHistorico() {
        Stack temp = (Stack) historico.clone();
        System.out.println("=== Histórico de Operações ===");
        while (!temp.empty()) {
            System.out.println("- " + temp.pop());
        }
    }
}

class Produto {
    private int id;
    private String nome;
    private double preco;
    
    public Produto(int id, String nome, double preco) {
        this.id = id;
        this.nome = nome;
        this.preco = preco;
    }
    
    public String getNome() { return nome; }
    public double getPreco() { return preco; }
}

class Pedido {
    private String cliente;
    private Produto produto;
    
    public Pedido(String cliente, Produto produto) {
        this.cliente = cliente;
        this.produto = produto;
    }
    
    @Override
    public String toString() {
        return cliente + " pediu " + produto.getNome() + 
               " (R$ " + produto.getPreco() + ")";
    }
}

// Uso
SistemaEstoque sistema = new SistemaEstoque();
sistema.adicionarProduto(1, "Notebook", 3000);
sistema.adicionarProduto(2, "Mouse", 50);

sistema.fazerPedido("João", 1);
sistema.fazerPedido("Maria", 2);

sistema.processarPedidos();
sistema.exibirHistorico();

11. Exercícios Práticos

Exercício 1: Inverter Lista

Crie uma função que receba um ArrayList e retorne uma nova lista com os elementos em ordem inversa.

Exercício 2: Validar Parênteses

Use uma Stack para validar se os parênteses de uma expressão estão balanceados.

Exercício 3: Remover Duplicatas

Crie uma função que receba uma lista com duplicatas e retorne um HashSet com valores únicos.

Exercício 4: Fila de Atendimento

Implemente um sistema de fila onde clientes entram, são atendidos e removidos da fila.

Exercício 5: Sistema de Cachê

Use HashMap para criar um sistema que armazene dados frequentes para acesso rápido.

12. Resumo - Estruturas Essenciais

📋 Array

Coleção fixa e tipada. Rápida para acesso, lenta para inserção/remoção.

📝 ArrayList

Lista dinâmica. Melhor para uso geral quando precisa flexibilidade.

🔗 LinkedList

Lista encadeada. Melhor para inserções/remoções frequentes no meio.

📞 Queue (Fila)

FIFO. Use para atendimento, processamento sequencial.

📚 Stack (Pilha)

LIFO. Use para histórico, backtracking, expressões.

🎯 Set

Valores únicos. Use para remover duplicatas, validações.

🗂️ Map

Chave-valor. Use para buscas rápidas, mapeamentos.

Conclusão

Dominar estruturas de dados é essencial para todo programador. A escolha correta pode fazer a diferença entre um programa eficiente e um programa que trava.

Cada estrutura tem seu propósito: Arrays para dados fixos, ArrayList para flexibilidade, Queue para FIFO, Stack para LIFO, HashMap para buscas rápidas.

Pratique com exemplos reais e você dominará estruturas de dados em Java! 🚀