8 Coleções e Listas
8.1 Por que usar lista
Quando queremos guardar várias mensagens, uma variável só não basta. Em aplicações reais, o volume de dados cresce com o uso, então precisamos de uma estrutura flexível. Uma lista permite inserir novos elementos sem definir um tamanho fixo no início.
A solução é usar ArrayList. Com ele, podemos armazenar dados em sequência e acessar cada item pelo índice. Esse tipo de coleção é muito comum em projetos com menus, cadastros e históricos.
8.2 Exemplo básico
No exemplo abaixo, criamos uma lista de textos e adicionamos duas mensagens. Cada chamada de add coloca um novo item no final da lista.
import java.util.ArrayList;
ArrayList<String> historico = new ArrayList<>();
historico.add("oi");
historico.add("qual é seu nome?");Perceba que usamos String entre < >, indicando que essa lista só aceita textos. Isso ajuda a evitar erros de tipo e deixa o código mais claro.
8.3 Percorrendo a lista
Depois de armazenar os dados, normalmente precisamos ler e exibir cada elemento. O laço for-each é uma forma simples e elegante de fazer isso.
for (String item : historico) {
System.out.println("Mensagem: " + item);
}Esse formato é útil quando queremos processar todos os itens, sem nos preocupar com índices. Se você precisar da posição de cada elemento, pode usar um for tradicional com contador.
8.4 Aplicando no chatbot
Aqui, a lista fica como atributo da classe para guardar o histórico da conversa. Sempre que o método responder é chamado, a mensagem recebida é adicionada ao histórico. Com isso, o chatbot pode evoluir para respostas baseadas no contexto das mensagens anteriores.
import java.util.ArrayList;
public class Chatbot {
private final ArrayList<String> historico = new ArrayList<>();
public String responder(String msg) {
historico.add(msg);
return "Recebi: " + msg;
}
public int totalMensagens() {
return historico.size();
}
}O método totalMensagens usa size() para retornar a quantidade atual de elementos. Esse tipo de método é útil para métricas, relatórios e validações no fluxo do programa.
8.5 Boas práticas
Escolha nomes de listas que indiquem claramente o conteúdo armazenado. Evite misturar responsabilidades: uma lista para mensagens, outra para usuários, por exemplo. Sempre valide entradas antes de adicionar dados, especialmente em sistemas com entrada do usuário. Quando necessário, limite o tamanho da lista para controlar o uso de memória.
8.6 Operações mais usadas no dia a dia
Depois de criar uma lista, as operações mais comuns são adicionar, ler, atualizar e remover elementos. No Java, essas ações aparecem com frequência em telas de cadastro, carrinhos de compra e históricos. Entender bem cada método evita erros e acelera o desenvolvimento.
import java.util.ArrayList;
ArrayList<String> tarefas = new ArrayList<>();
tarefas.add("Estudar Java"); // adiciona no final
tarefas.add("Revisar listas"); // adiciona no final
tarefas.add(1, "Praticar exercícios"); // adiciona na posição 1
System.out.println(tarefas.get(0)); // lê o item da posição 0
tarefas.set(0, "Estudar Java 30 min"); // atualiza item existente
tarefas.remove(2); // remove pelo índice
tarefas.remove("Praticar exercícios"); // remove pelo valorAo remover por índice, os elementos seguintes se deslocam para a esquerda. Isso significa que a posição dos itens muda, e o programa precisa considerar esse comportamento.
8.7 size, isEmpty e contains
Em fluxos reais, quase sempre você precisa decidir se há dados antes de processar. Os métodos size(), isEmpty() e contains() ajudam nessas verificações básicas.
if (tarefas.isEmpty()) {
System.out.println("Nenhuma tarefa cadastrada.");
} else {
System.out.println("Total: " + tarefas.size());
}
if (tarefas.contains("Estudar Java 30 min")) {
System.out.println("Tarefa encontrada.");
}Esse padrão evita tentativas de acesso a posições inexistentes e melhora a robustez do código.
8.8 Tipos, generics e autoboxing
Uma lista pode armazenar objetos de qualquer tipo, desde que você declare o tipo entre < >. Isso é chamado de generics e garante mais segurança de tipos em tempo de compilação.
Para números inteiros, por exemplo, usamos Integer em vez de int. O Java converte automaticamente entre int e Integer em muitos cenários, processo conhecido como autoboxing.
ArrayList<Integer> notas = new ArrayList<>();
notas.add(8); // int -> Integer (autoboxing)
notas.add(10);
int primeiraNota = notas.get(0); // Integer -> int (unboxing)
System.out.println(primeiraNota);Evite usar listas sem tipo, como ArrayList lista = new ArrayList();, pois isso facilita erros difíceis de identificar.
8.9 Cuidados com índice e exceções
O índice da lista sempre começa em 0. Se você tentar acessar uma posição fora do intervalo válido, ocorre IndexOutOfBoundsException.
ArrayList<String> nomes = new ArrayList<>();
nomes.add("Ana");
// nomes.get(1); // erro: só existe o índice 0Antes de acessar um índice, garanta que ele está entre 0 e size() - 1. Esse cuidado simples reduz falhas em tempo de execução.
8.10 Ordenando elementos
Listas também permitem ordenação, algo útil para exibir dados em ordem alfabética ou numérica. Com Collections.sort, você ordena a própria lista.
import java.util.ArrayList;
import java.util.Collections;
ArrayList<String> alunos = new ArrayList<>();
alunos.add("Carla");
alunos.add("Bruno");
alunos.add("Ana");
Collections.sort(alunos);
for (String aluno : alunos) {
System.out.println(aluno);
}Depois de ordenar, a lista original fica modificada. Se precisar manter a ordem inicial, faça uma cópia antes de ordenar.
8.11 Lista no projeto de chatbot
No chatbot, você pode usar a lista para mais do que armazenar mensagens. Também é possível criar regras simples baseadas no histórico recente.
public String ultimaMensagem() {
if (historico.isEmpty()) {
return "Sem mensagens ainda.";
}
return historico.get(historico.size() - 1);
}Com esse método, fica fácil inspecionar contexto e construir respostas progressivamente melhores. Esse é um passo importante para evoluir de um chatbot estático para um chatbot com memória de conversa.
- Mostre o total de mensagens no final.
- Imprima todo o histórico.
- Guarde apenas as últimas 10 mensagens.
- Crie um método que retorne a última mensagem digitada.
- Ordene uma lista de nomes e exiba o resultado.
- Valide o índice antes de acessar qualquer posição da lista.
Ao estudar coleções, procure sempre relacionar cada método ao problema que você quer resolver. Quando essa relação fica clara, seu código melhora, porque as estruturas de dados deixam de ser teoria e passam a ser ferramenta prática.
Uma boa estratégia de estudo é comparar implementações simples com e sem lista. Esse contraste ajuda a perceber por que coleções tornam o programa mais escalável, legível e fácil de manter.
Com o tempo, você passa a escolher estruturas com mais critério técnico. Essa habilidade é essencial para evoluir em Java e construir sistemas mais confiáveis.