Java A Classe Collections
A classe Collections (no plural) é uma caixa de ferramentas para coleções: ordenar, inverter, embaralhar, achar o maior, contar repetições, criar listas "só de leitura"… São métodos prontos que você chama sem criar objeto nenhum, como faz com Math. Ela é a irmã da classe Arrays, que faz o mesmo para arrays.
- Reorganizar listas:
sort,reverse,swapeshuffle(com e sem semente). - Perguntar:
max,minefrequency. - Proteger:
unmodifiableList(e a diferença paraList.copyOf). - Criar:
emptyList,nCopies,filleaddAll. - Os erros:
NoSuchElementExceptioneUnsupportedOperationException.
A ideia: métodos estáticos
Até aqui, você chamava métodos na coleção: lista.add(…), lista.size(). Os métodos de Collections são estáticos: você chama na classe e passa a coleção como argumento:
lista.sort(null); « método da lista Collections.sort(lista); « método da classe Collections, recebendo a lista
Collection × Collections
Collection (singular) é a interface que List, Set e Queue herdam (veja a Visão Geral). Collections (plural) é esta classe de ferramentas. Um "s" de diferença; o compilador não perdoa a troca.
Muitos desses métodos só funcionam com List (que tem posições): ordenar, inverter e embaralhar um HashSet não faria sentido. Já max, min e frequency aceitam qualquer Collection.
Reorganizar: sort, reverse, swap
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> notas = new ArrayList<>(List.of(7, 10, 3, 8, 5));
System.out.println("original: " + notas);
Collections.sort(notas);
System.out.println("sort: " + notas);
Collections.reverse(notas);
System.out.println("reverse: " + notas);
Collections.sort(notas, Collections.reverseOrder());
System.out.println("decrescente: " + notas);
Collections.swap(notas, 0, 4);
System.out.println("swap(0, 4): " + notas);
}
}
$ javac Main.java $ java Main original: [7, 10, 3, 8, 5] sort: [3, 5, 7, 8, 10] reverse: [10, 8, 7, 5, 3] decrescente: [10, 8, 7, 5, 3] swap(0, 4): [3, 8, 7, 5, 10]
| Método | Tradução | O que faz |
|---|---|---|
sort(lista) | ordenar | Ordem natural (menor → maior, A → Z). Muda a própria lista. |
sort(lista, comparador) | Ordem definida pelo comparador. Collections.reverseOrder() = decrescente. | |
reverse(lista) | inverter | Põe de trás para a frente. Não ordena: só inverte a ordem atual. |
swap(lista, i, j) | trocar | Troca os elementos das posições i e j. |
Aqui reverse e o sort decrescente deram o mesmo resultado só porque a lista já estava ordenada. Numa lista desordenada, reverse([7, 10, 3]) dá [3, 10, 7]: é só um espelho. Lembre também que ordenar textos pela ordem natural põe as maiúsculas antes das minúsculas (veja a aula de ArrayList).
Embaralhar: shuffle (com e sem semente)
A Ana quer sortear a ordem de apresentação dos trabalhos. Collections.shuffle ("embaralhar") mistura a lista como um baralho:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class Main {
public static void main(String[] args) {
List<String> alunos = new ArrayList<>(List.of("Ana", "Bruno", "Carla", "Duda", "Edu"));
Collections.shuffle(alunos);
System.out.println("shuffle sem semente: " + alunos);
Collections.sort(alunos);
Collections.shuffle(alunos, new Random(42));
System.out.println("shuffle semente 42: " + alunos);
Collections.sort(alunos);
Collections.shuffle(alunos, new Random(42));
System.out.println("de novo, semente 42: " + alunos);
}
}
O mesmo programa, rodado três vezes:
$ javac Main.java $ java Main shuffle sem semente: [Edu, Ana, Duda, Carla, Bruno] shuffle semente 42: [Bruno, Carla, Duda, Edu, Ana] de novo, semente 42: [Bruno, Carla, Duda, Edu, Ana] $ java Main shuffle sem semente: [Duda, Bruno, Edu, Carla, Ana] « mudou shuffle semente 42: [Bruno, Carla, Duda, Edu, Ana] « igual de novo, semente 42: [Bruno, Carla, Duda, Edu, Ana] $ java Main shuffle sem semente: [Ana, Edu, Bruno, Carla, Duda] « mudou de novo shuffle semente 42: [Bruno, Carla, Duda, Edu, Ana] « sempre igual de novo, semente 42: [Bruno, Carla, Duda, Edu, Ana]
- Collections.shuffle(alunos)
- Embaralha de um jeito diferente a cada execução. É o que você quer num sorteio de verdade.
- new Random(42)
- Um gerador de números aleatórios com semente 42 (veja Números Aleatórios). Com a mesma semente, o gerador produz sempre a mesma sequência.
- Collections.shuffle(alunos, new Random(42))
- Embaralha usando esse gerador: a mesma lista de entrada dá sempre o mesmo resultado.
- Collections.sort(alunos) antes
- Garante que a entrada do segundo e do terceiro
shuffleé a mesma (em ordem alfabética). Embaralhar uma lista já embaralhada daria outra coisa.
Por que alguém quer um embaralhamento "previsível"? Para testes (o resultado tem que ser conferível), para repetir um problema que aconteceu e para jogos em que a "partida número 42" deve ser sempre a mesma. É assim também que as missões deste guia conseguem conferir a sua saída.
Missão: sorteie a ordem de apresentação com Collections.shuffle e semente 42, e mostre a ordem numerada (1º Bruno, 2º …). Depois tire a semente e rode algumas vezes.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class Main {
public static void main(String[] args) {
List<String> alunos = new ArrayList<>(List.of("Ana", "Bruno", "Carla", "Duda", "Edu"));
// embaralhe aqui
for (int i = 0; i < alunos.size(); i++) {
System.out.println((i + 1) + "º " + alunos.get(i));
}
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class Main {
public static void main(String[] args) {
List<String> alunos = new ArrayList<>(List.of("Ana", "Bruno", "Carla", "Duda", "Edu"));
Collections.shuffle(alunos, new Random(42));
for (int i = 0; i < alunos.size(); i++) {
System.out.println((i + 1) + "º " + alunos.get(i));
}
}
}
Perguntar: max, min, frequency
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> notas = List.of(7, 10, 3, 8, 5, 10, 7, 10);
System.out.println("max: " + Collections.max(notas));
System.out.println("min: " + Collections.min(notas));
System.out.println("frequency(10): " + Collections.frequency(notas, 10));
System.out.println("frequency(9): " + Collections.frequency(notas, 9));
List<String> nomes = List.of("Ana", "Bruno", "Bia", "Carla");
System.out.println("max (alfabética): " + Collections.max(nomes));
System.out.println("max (tamanho): " + Collections.max(nomes, Comparator.comparing(String::length)));
}
}
$ javac Main.java $ java Main max: 10 min: 3 frequency(10): 3 « o 10 aparece três vezes frequency(9): 0 max (alfabética): Carla « o "último" no dicionário max (tamanho): Bruno « Bruno e Carla têm 5 letras: vale o primeiro
| Método | O que faz |
|---|---|
max(col) / min(col) | O maior / o menor pela ordem natural. Não precisa ordenar antes, e não muda a coleção. |
max(col, comparador) | O maior segundo o comparador (aqui, pelo tamanho). Empate: devolve o primeiro encontrado. |
frequency(col, x) | Quantas vezes x aparece (compara com equals). |
Repare que funcionou numa List.of, que é imutável: esses métodos só leem.
Proteger: unmodifiableList
Às vezes você quer entregar uma lista para outra parte do programa só para leitura. Collections.unmodifiableList(lista) devolve uma "janela" para a lista original, sem a porta de mexer:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> chamada = new ArrayList<>(List.of("Ana", "Bruno"));
List<String> soLeitura = Collections.unmodifiableList(chamada);
List<String> copia = List.copyOf(chamada);
System.out.println("soLeitura: " + soLeitura);
chamada.add("Carla");
System.out.println("depois de chamada.add: soLeitura = " + soLeitura + ", copia = " + copia);
soLeitura.add("Duda");
}
}
$ javac Main.java $ java Main soLeitura: [Ana, Bruno] depois de chamada.add: soLeitura = [Ana, Bruno, Carla], copia = [Ana, Bruno] Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1093) at Main.main(Main.java:15)
Três coisas aconteceram:
- A Carla, acrescentada na lista original, apareceu na
soLeitura: ela é uma janela, não uma cópia. - A
copia(feita comList.copyOf, Java 10+) continuou[Ana, Bruno]: é uma fotografia, imutável e independente. - Tentar mudar pela janela dá
UnsupportedOperationException.
| Forma | Pode mudar por ela? | Vê mudanças da original? | Quando usar |
|---|---|---|---|
Collections.unmodifiableList(l) | Não | Sim | Dar acesso de leitura a uma lista que continua mudando (um getAlunos() de uma classe; veja Encapsulamento). |
List.copyOf(l) | Não | Não | Guardar um retrato que nunca muda. |
new ArrayList<>(l) | Sim | Não | Ter a sua própria versão para mexer. |
Existem também unmodifiableSet e unmodifiableMap, com a mesma ideia.
Criar: emptyList, nCopies, fill, addAll
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> vazia = Collections.emptyList();
System.out.println("emptyList: " + vazia + " size " + vazia.size());
List<String> tracos = Collections.nCopies(5, "-");
System.out.println("nCopies(5, \"-\"): " + tracos);
System.out.println(String.join("", Collections.nCopies(20, "=")));
List<Integer> zeros = new ArrayList<>(Collections.nCopies(4, 0));
zeros.set(2, 7);
System.out.println("placar: " + zeros);
Collections.fill(zeros, 1);
System.out.println("fill(1): " + zeros);
List<String> frutas = new ArrayList<>();
Collections.addAll(frutas, "uva", "kiwi", "pera");
System.out.println("addAll: " + frutas);
}
}
$ javac Main.java $ java Main emptyList: [] size 0 nCopies(5, "-"): [-, -, -, -, -] ==================== « String.join colou as 20 cópias placar: [0, 0, 7, 0] fill(1): [1, 1, 1, 1] addAll: [uva, kiwi, pera]
- Collections.emptyList()
- Uma lista vazia e imutável. Útil para um método devolver "nada" sem devolver
null(quem recebe pode fazer for-each nela sem erro). O mesmo queList.of(). - Collections.nCopies(5, "-")
- "n cópias": uma lista imutável com o mesmo elemento repetido.
- new ArrayList<>(Collections.nCopies(4, 0))
- O truque para criar uma lista mutável já com tamanho e valor inicial: um placar com 4 zeros.
- Collections.fill(zeros, 1)
- "Preencher": troca todos os elementos existentes por
1. Não muda o tamanho. - Collections.addAll(frutas, "uva", "kiwi", "pera")
- Acrescenta vários de uma vez, numa coleção que já existe.
Quando dá errado
NoSuchElementException no max de uma coleção vazia
Este código tem um erro de propósito.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> notas = new ArrayList<>();
System.out.println(Collections.max(notas));
}
}
$ javac Main.java $ java Main Exception in thread "main" java.util.NoSuchElementException at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1053) at java.base/java.util.Collections.max(Collections.java:698) at Main.main(Main.java:8)
O que quer dizer: não existe maior elemento numa lista vazia. A pilha mostra que o max usa um iterador e pediu o primeiro elemento, que não existe.
Como consertar: confira isEmpty() antes. Com streams, o max devolve um Optional, que trata o "vazio" sem exceção.
UnsupportedOperationException ao mudar uma nCopies
Este código tem um erro de propósito.
List<String> tracos = Collections.nCopies(3, "-");
tracos.set(0, "+");
$ java Main Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.AbstractList.set(AbstractList.java:138) at Main.main(Main.java:7)
O que quer dizer: nCopies, emptyList, unmodifiableList (e List.of) devolvem listas imutáveis.
Como consertar: copie para uma ArrayList: new ArrayList<>(Collections.nCopies(3, "-")).
Collection.sort
Este código tem um erro de propósito.
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Integer> notas = new ArrayList<>(List.of(7, 3));
Collection.sort(notas);
}
}
$ javac Main.java
Main.java:6: error: cannot find symbol
Collection.sort(notas);
^
symbol: method sort(List<Integer>)
location: interface Collection
1 error
O que aconteceu: faltou o "s". O javac procurou um método sort na interface Collection, e ela não tem.
Como consertar: Collections.sort(…), ou lista.sort(null).
Referência
| Método | Aceita | O que faz |
|---|---|---|
sort(l) / sort(l, comp) | List | Ordena (muda a lista) |
reverse(l) | List | Inverte a ordem atual |
shuffle(l) / shuffle(l, new Random(s)) | List | Embaralha (com semente: sempre igual) |
swap(l, i, j) | List | Troca duas posições |
max(c) / min(c) (+ comparador) | Collection | Maior / menor; vazia: NoSuchElementException |
frequency(c, x) | Collection | Quantas vezes x aparece |
reverseOrder() | — | Comparador decrescente |
unmodifiableList/Set/Map(x) | List, Set, Map | Janela só de leitura |
emptyList(), emptySet(), emptyMap() | — | Coleção vazia imutável |
nCopies(n, x) | — | Lista imutável com n cópias de x |
fill(l, x) | List | Troca todos por x |
addAll(c, a, b, …) | Collection | Acrescenta vários |
disjoint(c1, c2) | Collection | true se não têm nenhum elemento em comum |
Exercícios
1. O maior
Complete para achar a maior nota da lista notas:
2. Quantas vezes
Complete para contar quantas vezes "Ana" aparece na lista presencas:
3. Sorteio repetível
Complete para embaralhar cartas sempre do mesmo jeito, com semente 7:
4. A lista é [7, 10, 3]. Como fica depois de Collections.reverse?
5. v = Collections.unmodifiableList(l). Depois, l.add("x"). O que acontece com v?
Missão final: mostre as estatísticas da turma usando Collections: Maior: 10, Menor: 3, Notas 10: 3 e o ranking em ordem decrescente, Ranking: [10, 10, 10, 8, 7, 7, 5, 3].
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> notas = new ArrayList<>(List.of(7, 10, 3, 8, 5, 10, 7, 10));
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> notas = new ArrayList<>(List.of(7, 10, 3, 8, 5, 10, 7, 10));
System.out.println("Maior: " + Collections.max(notas));
System.out.println("Menor: " + Collections.min(notas));
System.out.println("Notas 10: " + Collections.frequency(notas, 10));
Collections.sort(notas, Collections.reverseOrder());
System.out.println("Ranking: " + notas);
}
}
Resumo
Collections(plural) é uma classe de métodos estáticos;Collection(singular) é a interface.sort,reverse,swapeshufflemudam a própria lista.shufflecomnew Random(semente)é repetível.max,minefrequencysó leem;max/minde coleção vazia:NoSuchElementException.unmodifiableListé uma janela só de leitura (vê as mudanças da original);List.copyOfé um retrato.emptyList,nCopiescriam listas imutáveis;filleaddAllmexem em listas existentes.
Próxima aula: Comparable e Comparator, para ordenar os seus próprios objetos do jeito que quiser.