1. O problema da ordem nas coleções clássicas
No Java sempre houve coleções que garantem a ordem dos elementos (por exemplo, ArrayList, LinkedList, LinkedHashSet, LinkedHashMap), e outras em que a ordem não é garantida (por exemplo, HashSet, HashMap). Mas todas elas tinham uma desvantagem em comum: embora algumas armazenem elementos em uma determinada ordem, as interfaces padrão (List, Set, Map) não forneciam métodos universais para acessar os primeiros e últimos elementos ou para inverter a ordem.
Por exemplo, se você tem um List<String>, pode obter o primeiro elemento com list.get(0), mas para Set ou Map esse truque não funciona — será preciso usar iteradores ou escrever código extra. Convenhamos, isso não é muito prático e não ajuda na legibilidade.
É como se você tivesse um armário com gavetas, mas, para pegar a primeira ou a última, tivesse que contar todas as gavetas manualmente toda vez! Seria ótimo ter alças especiais “primeira” e “última”, não é?
O surgimento de SequencedCollection e interfaces relacionadas
No Java 21 surgiram novas interfaces de coleções:
- SequencedCollection<E>
- SequencedSet<E>
- SequencedMap<K, V>
Essas interfaces estendem as coleções padrão e introduzem uma abordagem unificada para trabalhar com a ordem dos elementos. Agora é possível escrever código universal para quaisquer coleções em que a ordem é importante, sem se preocupar com a implementação concreta.
O que é isso?
- SequencedCollection — é uma coleção em que os elementos têm uma ordem definida e você pode obter facilmente o primeiro e o último elemento, além de inverter a ordem.
- SequencedSet — o mesmo, mas para conjuntos (elementos únicos).
- SequencedMap — o mesmo, mas para mapas (chave-valor).
Quais coleções agora as implementam?
No Java 21, as novas interfaces são implementadas pelas seguintes coleções padrão:
- ArrayList, LinkedList → SequencedCollection
- LinkedHashSet, TreeSet → SequencedSet
- LinkedHashMap, TreeMap → SequencedMap
Isso significa que, se você já usa essas coleções, você automaticamente ganha novos recursos!
2. Métodos principais de SequencedCollection, SequencedSet, SequencedMap
Métodos de SequencedCollection
E getFirst(); // Obter o primeiro elemento
E getLast(); // Obter o último elemento
SequencedCollection<E> reversed(); // Obter a coleção em ordem inversa
Métodos de SequencedSet
Os mesmos métodos que em SequencedCollection, além de tudo o que Set faz.
Métodos de SequencedMap
Map.Entry<K, V> firstEntry(); // Primeira entrada (chave-valor)
Map.Entry<K, V> lastEntry(); // Última entrada (chave-valor)
SequencedMap<K, V> reversed(); // Map em ordem inversa
3. Exemplos de uso das novas interfaces
Exemplo 1: Obtendo o primeiro e o último elemento
import java.util.*;
public class SequencedDemo {
public static void main(String[] args) {
SequencedCollection<String> sc = new ArrayList<>();
sc.add("Java");
sc.add("Python");
sc.add("Kotlin");
// Obter o primeiro e o último elemento
String first = sc.getFirst(); // "Java"
String last = sc.getLast(); // "Kotlin"
System.out.println("Primeiro: " + first);
System.out.println("Último: " + last);
}
}
Saída:
Primeiro: Java
Último: Kotlin
Exemplo 2: Invertendo a coleção
SequencedCollection<String> sc = new LinkedList<>();
sc.add("A");
sc.add("B");
sc.add("C");
SequencedCollection<String> reversed = sc.reversed();
System.out.println(reversed); // [C, B, A]
Atenção: reversed() retorna uma visão da coleção em ordem inversa. Se você alterar a coleção original, a visão reversed também mudará!
Exemplo 3: Trabalhando com SequencedSet
SequencedSet<Integer> set = new LinkedHashSet<>();
set.add(100);
set.add(200);
set.add(300);
System.out.println("Primeiro elemento: " + set.getFirst()); // 100
System.out.println("Último elemento: " + set.getLast()); // 300
SequencedSet<Integer> reversedSet = set.reversed();
System.out.println(reversedSet); // [300, 200, 100]
Exemplo 4: Trabalhando com SequencedMap
SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.put("apple", 5);
map.put("banana", 3);
map.put("cherry", 7);
Map.Entry<String, Integer> first = map.firstEntry();
Map.Entry<String, Integer> last = map.lastEntry();
System.out.println("Primeiro: " + first.getKey() + " = " + first.getValue()); // apple = 5
System.out.println("Último: " + last.getKey() + " = " + last.getValue()); // cherry = 7
SequencedMap<String, Integer> reversedMap = map.reversed();
System.out.println(reversedMap); // {cherry=7, banana=3, apple=5}
4. O que isso tem a ver com o seu aplicativo?
Suponha que, no seu aplicativo educacional, você mantenha uma lista de usuários que fizeram login e queira obter rapidamente o primeiro e o último usuário (por exemplo, para mostrar “quem entrou primeiro” e “quem foi o último”). Antes era preciso escrever algo como:
List<String> users = new ArrayList<>();
// ... adicionamos usuários
String first = users.get(0);
String last = users.get(users.size() - 1);
Mas se a coleção não for uma lista, e sim, por exemplo, um LinkedHashSet (em que os elementos são únicos e a ordem é preservada), esse truque não vai funcionar:
Set<String> users = new LinkedHashSet<>();
// ... adicionamos usuários
// Como obter o primeiro? Apenas via iterador:
String first = users.iterator().next();
// E o último? Só percorrendo todos os elementos!
Agora é tudo mais simples e mais universal:
SequencedSet<String> users = new LinkedHashSet<>();
// ... adicionamos usuários
String first = users.getFirst();
String last = users.getLast();
Isso não apenas reduz a quantidade de código, como também o torna mais legível e seguro.
5. Diagrama das novas interfaces
classDiagram
Collection <|-- SequencedCollection
List <|-- SequencedCollection
Set <|-- SequencedSet
Map <|-- SequencedMap
SequencedCollection <|-- SequencedSet
SequencedSet <|-- LinkedHashSet
SequencedSet <|-- TreeSet
SequencedCollection <|-- ArrayList
SequencedCollection <|-- LinkedList
SequencedMap <|-- LinkedHashMap
SequencedMap <|-- TreeMap
6. Nuances úteis
Vantagens práticas de SequencedCollection
- Interface unificada para trabalhar com a ordem: Você não precisa mais lembrar onde existe get(0), onde é necessário um iterador e onde não é possível obter o primeiro elemento.
- Conveniência ao trabalhar com filas e pilhas: É fácil obter o primeiro (head) e o último (tail) elemento.
- Segurança e legibilidade: Menos erros relacionados ao uso incorreto de coleções; o código se torna autoexplicativo.
- Inversão rápida da coleção: O método reversed() permite obter facilmente a ordem inversa sem manipulações manuais.
- Facilidade de manutenção e evolução do código: Se no futuro você quiser substituir, por exemplo, um ArrayList por um LinkedHashSet, o código que usa SequencedCollection não precisará ser reescrito.
Particularidades de implementação
- Nem todas as coleções implementam SequencedCollection: Por exemplo, HashSet e HashMap não garantem ordem, portanto não implementam as novas interfaces.
- Os métodos podem lançar exceções: Se a coleção estiver vazia, chamar getFirst() ou getLast() resultará em NoSuchElementException. Não se esqueça de verificar se a coleção não está vazia!
- reversed() — é uma visão, não uma cópia: Alterações na coleção original se refletem na visão invertida e vice-versa.
- Compatibilidade: As novas interfaces estão disponíveis apenas a partir do Java 21. Se você usa uma versão mais antiga do Java, esses recursos ainda não estão disponíveis (mas é um ótimo motivo para atualizar!).
- Generics: Todas as novas interfaces suportam totalmente generics, então é possível trabalhar com quaisquer tipos de dados.
7. Erros comuns ao trabalhar com SequencedCollection
Erro nº 1: Esperar suporte a ordem de coleções que não a garantem. Se você tentar fazer cast de HashSet para SequencedSet, terá um erro de compilação — o HashSet não tem ordem e não implementa essa interface.
Erro nº 2: Ignorar coleções vazias. Chamar getFirst() ou getLast() em uma coleção vazia lançará uma exceção. Antes de chamar esses métodos, verifique se a coleção não está vazia:
if (!sc.isEmpty()) {
String first = sc.getFirst();
}
Erro nº 3: Mal-entendidos com reversed(). O método reversed() retorna uma visão, não uma cópia. Se você alterar a visão reversed, a coleção original mudará (e vice-versa). Isso pode levar a resultados inesperados se você não esperar esse comportamento.
Erro nº 4: Usar as novas interfaces em versões antigas do Java. Se seu projeto for compilado com uma versão abaixo do Java 21, o compilador não encontrará essas interfaces. Verifique a versão do JDK nas configurações do projeto!
GO TO FULL VERSION