Collections Framework em Java
O Collections Framework é um dos pilares da linguagem Java, projetado para facilitar o trabalho com grupos de objetos de maneira eficiente e organizada. Ele oferece um conjunto de interfaces, classes e métodos que permitem armazenar, acessar, e manipular dados de forma rápida e consistente. Assim como uma caixa de ferramentas ajuda a organizar e resolver diferentes tipos de problemas, o Collections Framework disponibiliza uma série de estruturas de dados (como listas, conjuntos, mapas e filas) que resolvem desafios comuns na programação.
Uma das grandes vantagens do Collections Framework é que ele simplifica e padroniza a forma como os desenvolvedores lidam com coleções de dados. Isso significa que você pode escolher a melhor estrutura para seu problema específico, sem precisar reinventar a roda, e ainda manter um código claro e fácil de manter. Seja manipulando listas de usuários, conjuntos de permissões ou mapas de produtos e preços, o Collections Framework é uma ferramenta essencial.
- Interfaces: As interfaces definem o comportamento esperado das coleções (como uma lista ou conjunto) e servem como a base para as classes que as implementam, assim o acesso aos objetos só pode ser feito por meio das interfaces, sem que o usuário ou alguém malicioso, consiga ver como de fato o objeto funciona.
- Implementação: As classes de implementação fornecem as versões concretas das interfaces. Elas definem como os dados são armazenados e manipulados na prática, ou seja, define como são feitos os acessos aos objetos
- Algoritmos: Os algoritmos no Collections Framework são métodos utilitários que realizam operações comuns em coleções, como busca, ordenação, modificação e manipulação de dados.
Interfaces
Collection
A interface raiz de todas as coleções no Java. Ela define operações básicas como adicionar, remover e verificar a existência de elementos. As subinterfaces como List
, Set
, e Queue
herdam essa interface. Métodos principais: add()
, remove()
, size()
, clear()
, contains()
, etc.
List
Uma coleção ordenada, que permite a inserção de elementos duplicados e acessa elementos por índices. A ordem dos elementos é mantida conforme a inserção. Exemplos de Implementações: ArrayList
, LinkedList
. Métodos principais: get(int index)
, add(int index, E element)
, remove(int index)
, indexOf(Object o)
.
Set
Uma coleção que não permite elementos duplicados. Os elementos de um Set
são únicos, ou seja, não há elementos repetidos. Exemplos de Implementações: HashSet
, TreeSet
. Métodos principais: Os mesmos de Collection
mas sem suporte para indexação (
add()
, remove(), etc...)
.
Queue
Representa uma fila que normalmente segue o comportamento FIFO (First In, First Out), onde os elementos são inseridos no final e removidos do início. Exemplos de Implementações: PriorityQueue. Métodos principais: offer(E e), poll(), peek(), remove().
Map
Ele lida com pares chave-valor. Cada chave é única e mapeia para um único valor. É útil para associações rápidas de dados, como em um dicionário. Exemplos de Implementações: HashMap, TreeMap. Métodos principais: put(K key, V value), get(Object key), remove(Object key), containsKey(Object key).
ArrayList
O ArrayList é uma das implementações mais comuns da interface List. Ele funciona como um array dinâmico, permitindo que o tamanho do array seja ajustado automaticamente à medida que novos elementos são adicionados ou removidos. Isso contrasta com os arrays tradicionais, que têm um tamanho fixo.
O ArrayList
permite o acesso a qualquer elemento de forma rápida e direta utilizando seu índice, o que o torna muito eficiente para operações de leitura, ele pode armazenar elementos duplicados, mantendo a ordem de inserção dos elementos.
Inserir ou remover elementos no meio do ArrayList
pode ser lento, pois todos os elementos subsequentes precisam ser deslocados para a esquerda ou direita, resultando em complexidade O(n).
LinkedList
O LinkedList é uma das implementações da interface List, a
o contrário do ArrayList, que utiliza um array dinâmico para armazenar seus elementos, o LinkedList é baseado em uma estrutura de dados de lista encadeada. Essa estrutura permite que cada elemento (ou nó) da lista mantenha referências para o próximo e, em alguns casos, para o anterior, formando uma cadeia de elementos. Inserir ou remover elementos em qualquer posição (exceto no final) é mais eficiente do que no ArrayList
, já que não requer deslocamento de elementos, e assim como o ArrayList
, o LinkedList
cresce e diminui de acordo com a necessidade, sem a necessidade de gerenciamento manual do tamanho.
O acesso a elementos por índice é mais lento em comparação com o ArrayList
, pois pode ser necessário percorrer a lista a partir do início (ou fim) para encontrar o elemento desejado, cada nó do LinkedList
consome mais memória devido às referências adicionais para o nó anterior e o próximo.
HashSet
O HashSet é uma das implementações da interface Set. Ele é projetado para armazenar elementos de forma não ordenada e única, ou seja, não permite elementos duplicados. Utilizando uma tabela de hash para organizar os elementos, o HashSet proporciona operações de inserção, remoção e busca muito eficientes, com tempo constante médio (O(1)).
Ao adicionar um elemento ao HashSet, um código hash é gerado, determinando a posição na tabela. Caso você tente adicionar um elemento que já existe, a operação não terá efeito, garantindo a unicidade dos elementos. Contudo, como a ordem dos elementos não é garantida, o HashSet não é a melhor escolha quando a ordem de inserção é importante. Além disso, ele permite apenas um valor nulo em sua coleção. A memória utilizada pelo HashSet pode ser maior do que outras implementações de conjuntos, mas sua eficiência em operações básicas o torna uma escolha popular quando se trabalha com coleções grandes de dados únicos.
TreeSet
O TreeSet é uma das implementações da interface Set, que armazena elementos em uma ordem específica, utilizando uma árvore binária de busca para organizar os dados. Essa estrutura de dados garante que os elementos estejam sempre ordenados, permitindo que você acesse o menor e o maior elemento rapidamente. O TreeSet também não permite elementos repetidos, e cada elemento deve ser único.
Por conta da ordenação dos elementos, o TreeSet pode consumir mais memória do que outras implementações como HashSet, mas oferece funcionalidades de ordenação que podem ser muito úteis em determinadas situações. Ele é ideal para cenários onde você precisa manter a ordem dos elementos ou realizar operações que dependem de uma sequência ordenada.
PriorityQueue
A PriorityQueue é uma das implementações da interface Queue. Ela armazena elementos de forma que o elemento com a maior prioridade seja sempre o primeiro a ser removido da fila. Isso significa que, ao contrário de uma fila convencional (FIFO - First In, First Out), onde os elementos são processados na ordem em que foram inseridos, a PriorityQueue organiza os elementos com base em suas prioridades. Os elementos são ordenados de acordo com um critério definido, que pode ser natural (se os elementos implementam a interface Comparable
) ou através de um Comparator
fornecido no momento da criação da PriorityQueue.
HashMap
O HashMap é uma das implementações da interface Map. Ele armazena pares de chave-valor, permitindo que você associe um valor a uma chave única. Essa estrutura de dados utiliza uma tabela de hash para armazenar os dados, o que proporciona operações de inserção, remoção e busca muito eficientes, geralmente em tempo constante (O(1)). Você pode usar qualquer objeto como chave, desde que ele implemente os métodos hashCode()
e equals()
corretamente, o HashMap permite uma única chave nula e múltiplos valores nulos.
O HashMap não mantém a ordem de inserção dos elementos. A ordem em que os pares chave-valor são iterados pode ser diferente da ordem em que foram adicionados.
TreeMap
O TreeMap é uma das implementações da interface Map e armazena pares de chave-valor de forma ordenada, utilizando uma árvore binária de busca como sua estrutura subjacente. Essa organização garante que as chaves sejam mantidas em ordem natural ou de acordo com um Comparator fornecido no momento da criação do TreeMap. Assim como em outras implementações de Map, as chaves no TreeMap devem ser únicas. Se você tentar adicionar uma nova chave que já existe, o valor associado será atualizado, os valores associados às chaves podem ser duplicados, permitindo múltiplas chaves diferentes com o mesmo valor, porem ele não permite chaves nulas, pois a ordenação das chaves requer que elas sejam comparáveis. No entanto, valores nulos são permitidos.
O TreeMap pode consumir mais memória do que o HashMap devido à estrutura da árvore e às referências adicionais necessárias para manter a ordem.
Referencias:
Josafá Marengo - Java Collections: Saiba quando usar Set, Map, List ou Queue. < https://josafa.com.br/blog/java-collections-saiba-quando-usar-set-map-list-ou-queue>
Java Collections: Como utilizar Collections. < https://www.devmedia.com.br/java-collections-como-utilizar-collections/18450>