A Interface Collection
Objective
Entender a interface Collection: a raiz genérica sobre a qual o Collections Framework é construído. Toda classe que define uma coleção precisa implementá-la (diretamente ou através de uma subinterface como List ou Set), e como Collection estende Iterable, toda coleção pode ser percorrida com um for-each.
Use Cases
- Escrever um método que aceita qualquer tipo de grupo de objetos —
ArrayList,HashSet,ArrayDeque, ... — declarando o parâmetro comoCollection<E>em vez de uma implementação específica. - Adicionar ou remover elementos um de cada vez (
add,remove) ou contra outra coleção em bloco (addAll,removeAll,retainAll). - Checar associação ou sobreposição entre duas coleções sem iterar manualmente (
contains,containsAll). - Converter uma coleção em array para interoperar com APIs baseadas em array (
toArray). - Processar elementos como um
Stream(stream,parallelStream) em vez de escrever um loop explícito. - Reconhecer, antes de chamar um mutador, que uma coleção produzida por uma factory como
List.of()é não modificável.
Deep Dive
Collection estende Iterable
javainterface Collection<E>Aqui, E especifica o tipo de objetos que a coleção vai armazenar. Collection estende Iterable, então só classes que implementam Collection (direta ou transitivamente) podem ser percorridas por um for-each, e qualquer classe que a implemente é obrigada a fornecer um iterator().
Adicionando elementos: add e addAll
javaCollection<String> names = new ArrayList<>();
names.add("Ann"); // boolean add(E obj)
Collection<String> more = new ArrayList<>(List.of("Bob", "Cid"));
names.addAll(more); // boolean addAll(Collection<? extends E> c)add() retorna false se a coleção não permite duplicatas e o objeto já é um membro (ex.: um Set); caso contrário retorna true. addAll() adiciona todo elemento de c à coleção que chamou o método e retorna true se essa coleção mudou como resultado.
Removendo elementos: remove, removeAll, retainAll, removeIf, clear
javanames.remove("Ann"); // remove one specific object
names.removeAll(List.of("Bob")); // remove every element also in c
names.retainAll(List.of("Cid")); // keep only elements also in c
names.removeIf(n -> n.length() > 3); // default method; remove those matching a Predicate
names.clear(); // empty the collection completelyremoveAll() calcula uma diferença de conjuntos (coleção que chamou o método menos c); retainAll() calcula uma interseção (só o que é compartilhado com c). Ambos retornam true se a coleção que chamou o método mudou.
Consultando: contains, containsAll, isEmpty, size, equals
javanames.contains("Cid"); // true if Cid is a member
names.containsAll(List.of("Cid", "Ann")); // true if all of these are members
names.isEmpty();
names.size();Duas coleções podem ser comparadas por igualdade com equals(), mas o significado exato de "igual" depende da subinterface implementadora — List se importa com a ordem dos elementos, Set não.
Iterando e transformando em stream
javaIterator<String> it = names.iterator(); // manual traversal
Stream<String> s = names.stream(); // default method
Stream<String> ps = names.parallelStream(); // default method, may run in parallel
Spliterator<String> sp = names.spliterator(); // default methodstream() retorna um stream que usa a coleção que chamou o método como sua fonte; parallelStream() retorna um que, se possível, divide sua fonte entre operações paralelas.
Convertendo para array: toArray()
toArray() tem três formas:
javaObject[] a1 = names.toArray(); // Object[] toArray()
String[] a2 = names.toArray(new String[0]); // <T> T[] toArray(T[] array)
String[] a3 = names.toArray(String[]::new); // default <T> T[] toArray(IntFunction<T[]> gen), JDK 11+A primeira forma sempre retorna Object[]. A segunda retorna um array com o tipo do array passado — mas esse parâmetro de tipo T é independente de E, então o compilador aceita um array do tipo de elemento errado, e a incompatibilidade só aparece em runtime:
javaCollection<String> names = List.of("Ann", "Bob");
Integer[] wrongType = names.toArray(new Integer[0]); // compiles; ArrayStoreException at runtimeColeções não modificáveis
Factory methods como List.of() retornam uma coleção fixa cujo conteúdo não pode ser alterado. Chamar qualquer mutador nela lança UnsupportedOperationException em vez de simplesmente não fazer nada silenciosamente:
javaCollection<String> fixed = List.of("Ann", "Bob");
fixed.add("Cid"); // UnsupportedOperationExceptionTrade-offs
removeAllvs.retainAllse leem de forma quase idêntica mas fazem o oposto —removeAll(c)mantém só os elementos que não estão emc(diferença), enquantoretainAll(c)mantém só os elementos que também estão emc(interseção). Usar o errado por hábito troca o resultado pelo seu complemento em vez de gerar um erro.Métodos opcionais falham em runtime, não em tempo de compilação —
add,removee os demais mutadores são declarados porCollection, mas uma implementação é livre para rejeitá-los. Chamar um deles em uma coleção não modificável compila sem problema e só lança exceção quando executado:javaCollection<String> fixed = List.of("a", "b"); fixed.add("c"); // UnsupportedOperationExceptionConsultas tipadas como
Objecttrocam segurança estática porClassCastException—contains,removee métodos similares aceitamObject, nãoE, então passar um valor de tipo incompatível compila sem reclamação e só falha quando a coleção de fato tenta compará-lo:javaCollection<String> set = new TreeSet<>(List.of("a", "b")); set.contains(42); // ClassCastException: Integer cannot be compared to StringtoArray(T[])compila para qualquer tipo de componente, correto ou não — como seu parâmetro de tipo não está vinculado ao tipo de elementoEda coleção, um array de tipo incompatível é aceito em tempo de compilação e só falha quando os elementos são de fato copiados para dentro dele:javaCollection<String> names = List.of("Ann", "Bob"); Integer[] wrongType = names.toArray(new Integer[0]); // ArrayStoreException