Article image
Ivo Correia
Ivo Correia17/04/2024 03:04
Share

Java Collections Framework, a base de todo dev Java!

  • #Estrutura de dados
  • #Lógica de Programação
  • #Java

O Java Collections Framework é uma parte fundamental do desenvolvimento em Java, e é utilizado em praticamente todos os programas Java. Introduzido na versão 1.2 do Java em 1998, o framework tem sido uma parte essencial da linguagem desde então. Ele fornece uma série de estruturas de dados para manipular coleções de objetos, como listas, conjuntos e mapas.

A escolha da estrutura de dados correta é crucial para o desempenho e eficiência de um programa Java. O Java Collections Framework oferece uma variedade de opções para atender a diferentes requisitos de aplicativos. Por exemplo, a interface List é ideal para armazenar elementos em uma ordem específica e permitir elementos duplicados, enquanto a interface Set é adequada para garantir a unicidade de elementos em uma coleção.

Com o lançamento do Java 8, o framework foi aprimorado com novos recursos para tornar a manipulação de coleções mais eficiente e conveniente. Isso incluiu a adição de métodos de streaming e operações de processamento de dados em coleções.

Em resumo, o Java Collections Framework é uma parte essencial do desenvolvimento em Java, oferecendo uma variedade de estruturas de dados para manipular coleções de objetos. Com suas interfaces bem projetadas e implementações eficientes, o framework facilita o desenvolvimento de aplicativos Java robustos e escaláveis.

Existem quatro tipos principais de coleções disponíveis no Java 8. Cada tipo possui suas próprias implementações nas interfaces correspondentes. No entanto, antes de nos aprofundarmos nessas implementações, é importante conhecer duas interfaces principais: Iterable e Collection. Embora você raramente precise lidar diretamente com elas, compreendê-las é fundamental.

A interface Iterable permite que você obtenha um iterador e percorra a sequência de elementos chamando o método next(). Além disso, ela facilita a iteração através do uso do loop "for-each", proporcionando um açúcar sintático para trabalhar com coleções:

image

A interface Collection estende Iterable e representa um grupo iterável de elementos que podem ser adicionados, removidos ou verificados quanto à presença na coleção. No entanto, as implementações geralmente implementam interfaces mais específicas projetadas tendo em mente a implementação da coleção. Aqui estão algumas das interfaces de Collection mais usadas:

List:

Uma coleção ordenada que permite elementos duplicados e oferece acesso aos elementos por meio de índices. Isso significa que os elementos são armazenados em uma ordem específica e podem ser acessados por suas posições na lista.

  • Implementações Comuns: ArrayList, LinkedList, Vector.
  • Características Importantes:Permite elementos duplicados.
  • Os elementos são acessados por meio de índices.
  • Oferece operações como adicionar, remover e acessar elementos por índice.

ArrayList:

ArrayList é uma implementação da interface List que utiliza um array para armazenar os elementos. Ele permite elementos duplicados e mantém a ordem de inserção dos elementos. Um ArrayList é redimensionado automaticamente conforme necessário. Ele fornece acesso rápido aos elementos por índice, mas pode ser mais lento para operações de inserção e remoção no meio da lista.

Exemplo de Aplicação:
import java.util.ArrayList;


public class ExemploArrayList {
  public static void main(String[] args) {
      // Criando um ArrayList de Integers
      ArrayList<Integer> lista = new ArrayList<>();


      // Adicionando elementos à lista
      lista.add(10);
      lista.add(20);
      lista.add(30);


      // Acessando o primeiro elemento
      int primeiroElemento = lista.get(0);
      System.out.println("Primeiro elemento: " + primeiroElemento);


      // Removendo o segundo elemento
      lista.remove(1);


      // Iterando sobre os elementos da lista
      System.out.println("Elementos da lista após remoção:");
      for (int numero : lista) {
          System.out.println(numero);
      }
  }
}

Neste exemplo, criamos um ArrayList de Integers e adicionamos alguns números a ele. A seguir, acessamos o primeiro elemento da lista utilizando o método get(), removemos o segundo elemento utilizando o método remove() e, por fim, iteramos sobre os elementos da lista utilizando um loop for-each.

LinkedList:

LinkedList é uma implementação da interface List que utiliza uma estrutura de lista duplamente encadeada para armazenar os elementos. Ela oferece operações eficientes de inserção e remoção em qualquer posição da lista, mas o acesso aleatório aos elementos pode ser mais lento em comparação com o ArrayList.

Exemplo de Aplicação:
import java.util.LinkedList;


public class ExemploLinkedList {
  public static void main(String[] args) {
      // Criando uma LinkedList de Strings
      LinkedList<String> lista = new LinkedList<>();


      // Adicionando elementos à lista
      lista.add("Maçã");
      lista.add("Banana");
      lista.add("Laranja");


      // Adicionando um elemento na primeira posição
      lista.addFirst("Pera");


      // Removendo o último elemento
      lista.removeLast();


      // Iterando sobre os elementos da lista
      System.out.println("Elementos da lista:");
      for (String fruta : lista) {
          System.out.println(fruta);
      }
  }
}

Neste exemplo, criamos uma LinkedList de Strings e adicionamos algumas frutas a ela. Em seguida, adicionamos a fruta "Pera" na primeira posição da lista utilizando o método addFirst(). Depois, removemos o último elemento da lista utilizando o método removeLast(). Por fim, iteramos sobre os elementos da lista utilizando um loop for-each.

Vector:

 Vector é uma implementação da interface List que é sincronizada e, portanto, é segura para uso em ambientes concorrentes. Ele é semelhante ao ArrayList, mas possui operações sincronizadas, o que o torna mais lento em comparação com o ArrayList em operações não sincronizadas. É recomendado para cenários onde a sincronização é necessária.

Exemplo de Aplicação:
import java.util.Vector;


public class ExemploVector {
  public static void main(String[] args) {
      // Criando um Vector de Integers
      Vector<Integer> vetor = new Vector<>();


      // Adicionando elementos ao vetor
      vetor.add(10);
      vetor.add(20);
      vetor.add(30);


      // Acessando o terceiro elemento
      int terceiroElemento = vetor.get(2);
      System.out.println("Terceiro elemento: " + terceiroElemento);


      // Removendo o segundo elemento
      vetor.remove(1);


      // Iterando sobre os elementos do vetor
      System.out.println("Elementos do vetor após remoção:");
      for (int numero : vetor) {
          System.out.println(numero);
      }
  }
}



Neste exemplo, criamos um Vector de Integers e adicionamos alguns números a ele. Em seguida, acessamos o terceiro elemento da lista utilizando o método get(), removemos o segundo elemento utilizando o método remove() e, por fim, iteramos sobre os elementos do vetor utilizando um loop for-each.

Set:

Uma coleção que não permite elementos duplicados e não garante a ordem dos elementos. Cada elemento em um conjunto deve ser único.

  • Implementações Comuns: HashSet, TreeSet, LinkedHashSet.
  • Características Importantes:Não permite elementos duplicados.
  • Não garante a ordem dos elementos.
  • É eficiente para verificar a presença de um elemento.
  • HashSet é geralmente a implementação mais rápida e TreeSet mantém os elementos em ordem natural ou por um comparador específico.

HashSet:

HashSet é uma implementação da interface Set que armazena elementos únicos e não garante a ordem dos elementos. Ele utiliza uma tabela hash para armazenar os elementos, o que resulta em operações de inserção, remoção e busca rápidas (complexidade O(1)), desde que a função de dispersão (hash function) seja bem distribuída.

Exemplo de Aplicação:
//Exemplo:
import java.util.HashSet;


public class ExemploHashSet {
  public static void main(String[] args) {
      // Criando um HashSet de Strings
      HashSet<String> conjunto = new HashSet<>();


      // Adicionando elementos ao conjunto
      conjunto.add("Maçã");
      conjunto.add("Banana");
      conjunto.add("Laranja");


      // Adicionando um elemento duplicado (será ignorado)
      conjunto.add("Maçã");


      // Verificando se o conjunto contém um elemento específico
      System.out.println("O conjunto contém Banana? " + conjunto.contains("Banana"));


      // Removendo um elemento do conjunto
      conjunto.remove("Laranja");


      // Iterando sobre os elementos do conjunto
      System.out.println("Elementos do conjunto:");
      for (String fruta : conjunto) {
          System.out.println(fruta);
      }
  }
}


Neste exemplo, criamos um HashSet de Strings e adicionamos algumas frutas a ele. Como HashSet não permite elementos duplicados, a segunda tentativa de adicionar "Maçã" é ignorada. Em seguida, verificamos se o conjunto contém a string "Banana", removemos a string "Laranja" e, por fim, iteramos sobre os elementos do conjunto.

Map:

Map é uma coleção que mapeia chaves únicas para valores correspondentes. Cada chave em um mapa deve ser única e é usada para recuperar o valor associado a ela.

image

  • Implementações Comuns: HashMap, TreeMap, LinkedHashMap.
  • Características Importantes:Cada chave é única no mapa.
  • Permite associar um valor a uma chave e recuperá-lo posteriormente.
  • HashMap é geralmente a implementação mais rápida e não garante a ordem dos elementos; TreeMap mantém os elementos em ordem natural ou por um comparador específico.

HashMap:

HashMap é uma implementação da interface Map que armazena pares chave-valor e não garante a ordem dos elementos. Ele é eficiente para operações de inserção, remoção e busca, proporcionando um acesso rápido aos elementos (complexidade O(1) em média). No entanto, a ordem dos elementos não é garantida.

Exemplo de Aplicação:
//Exemplo
import java.util.HashMap;


public class ExemploHashMap {
  public static void main(String[] args) {
      // Criando um HashMap com chave String e valor Integer
      HashMap<String, Integer> mapa = new HashMap<>();


      // Adicionando elementos ao mapa
      mapa.put("Maçã", 10);
      mapa.put("Banana", 20);
      mapa.put("Laranja", 30);


      // Acessando o valor associado a uma chave
      int quantidadeDeMacas = mapa.get("Maçã");
      System.out.println("Quantidade de maçãs: " + quantidadeDeMacas);


      // Removendo um par chave-valor do mapa
      mapa.remove("Banana");


      // Iterando sobre os pares chave-valor do mapa
      System.out.println("Pares chave-valor do mapa:");
      for (String chave : mapa.keySet()) {
          int valor = mapa.get(chave);
          System.out.println(chave + ": " + valor);
      }
  }
}

Neste exemplo, criamos um HashMap com chaves do tipo String e valores do tipo Integer. Adicionamos alguns pares chave-valor ao mapa utilizando o método put(). Em seguida, acessamos o valor associado à chave "Maçã" utilizando o método get(), removemos o par chave-valor associado à chave "Banana" utilizando o método remove(), e, por fim, iteramos sobre os pares chave-valor do mapa utilizando um loop for-each sobre o conjunto de chaves (keySet()).

TreeMap:

TreeMap é uma implementação da interface Map que armazena pares chave-valor em uma estrutura de árvore balanceada. Ele mantém os elementos ordenados com base na ordem natural das chaves ou em um comparador fornecido. TreeMap oferece uma ordem garantida dos elementos, tornando-se útil em situações onde a ordenação dos elementos é importante.

Exemplo de Aplicação:
import java.util.TreeMap;


public class ExemploTreeMap {
  public static void main(String[] args) {
      // Criando um TreeMap com chave String e valor Integer
      TreeMap<String, Integer> mapa = new TreeMap<>();


      // Adicionando elementos ao mapa
      mapa.put("Maçã", 10);
      mapa.put("Banana", 20);
      mapa.put("Laranja", 30);


      // Acessando o valor associado a uma chave
      int quantidadeDeMacas = mapa.get("Maçã");
      System.out.println("Quantidade de maçãs: " + quantidadeDeMacas);


      // Removendo um par chave-valor do mapa
      mapa.remove("Banana");


      // Iterando sobre os pares chave-valor do mapa
      System.out.println("Pares chave-valor do mapa:");
      for (String chave : mapa.keySet()) {
          int valor = mapa.get(chave);
          System.out.println(chave + ": " + valor);
      }
  }
}


Neste exemplo, criamos um TreeMap com chaves do tipo String e valores do tipo Integer. Adicionamos alguns pares chave-valor ao mapa utilizando o método put(). Em seguida, acessamos o valor associado à chave "Maçã" utilizando o método get(), removemos o par chave-valor associado à chave "Banana" utilizando o método remove(), e, por fim, iteramos sobre os pares chave-valor do mapa utilizando um loop for-each sobre o conjunto de chaves (keySet()).

LinkedHashMap:

LinkedHashMap é uma implementação da interface Map que mantém a ordem de inserção dos elementos. Ele combina as características de um HashMap e de uma LinkedList, ou seja, oferece a rápida busca por meio de uma tabela hash e mantém uma lista duplamente encadeada para manter a ordem de inserção dos elementos.

Exemplo de Aplicação:
import java.util.LinkedHashMap;


public class ExemploLinkedHashMap {
  public static void main(String[] args) {
      // Criando um LinkedHashMap com chave String e valor Integer
      LinkedHashMap<String, Integer> mapa = new LinkedHashMap<>();


      // Adicionando elementos ao mapa
      mapa.put("Maçã", 10);
      mapa.put("Banana", 20);
      mapa.put("Laranja", 30);


      // Acessando o valor associado a uma chave
      int quantidadeDeMacas = mapa.get("Maçã");
      System.out.println("Quantidade de maçãs: " + quantidadeDeMacas);


      // Removendo um par chave-valor do mapa
      mapa.remove("Banana");


      // Iterando sobre os pares chave-valor do mapa
      System.out.println("Pares chave-valor do mapa:");
      for (String chave : mapa.keySet()) {
          int valor = mapa.get(chave);
          System.out.println(chave + ": " + valor);
      }
  }
}


Neste exemplo, criamos um LinkedHashMap com chaves do tipo String e valores do tipo Integer. Adicionamos alguns pares chave-valor ao mapa utilizando o método put(). Em seguida, acessamos o valor associado à chave "Maçã" utilizando o método get(), removemos o par chave-valor associado à chave "Banana" utilizando o método remove(), e, por fim, iteramos sobre os pares chave-valor do mapa utilizando um loop for-each sobre o conjunto de chaves (keySet()).

Queue:

Uma coleção ordenada usada para armazenar elementos antes de serem processados, seguindo o princípio FIFO (First-In-First-Out). O primeiro elemento inserido na fila é o primeiro a ser removido.

  • Implementações Comuns: LinkedList, PriorityQueue, ArrayDeque.
  • Características Importantes:Segue o princípio FIFO.
  • Pode ser usado em situações em que a ordem de chegada é importante, como em filas de tarefas a serem processadas.
  • LinkedList é uma implementação clássica, enquanto PriorityQueue permite ordenar os elementos com base em uma ordem definida.

PriorityQueue:

PriorityQueue é uma implementação da interface Queue que mantém os elementos em uma ordem específica definida por um comparador ou pela ordem natural dos elementos. Os elementos são inseridos na fila com base na prioridade e removidos de acordo com essa prioridade. Esta classe não permite elementos nulos.

Exemplo de Aplicação:
//Exemplo:
import java.util.PriorityQueue;


public class ExemploPriorityQueue {
  public static void main(String[] args) {
      // Criando uma PriorityQueue de Integers
      PriorityQueue<Integer> filaPrioridade = new PriorityQueue<>();


      // Adicionando elementos à fila de prioridade
      filaPrioridade.offer(30);
      filaPrioridade.offer(20);
      filaPrioridade.offer(40);
      filaPrioridade.offer(10);


      // Removendo e exibindo os elementos da fila de prioridade
      while (!filaPrioridade.isEmpty()) {
          System.out.println(filaPrioridade.poll());
      }
  }
}


Neste exemplo, criamos uma PriorityQueue de Integers e adicionamos alguns números a ela usando o método offer(). Os números são automaticamente organizados em ordem ascendente devido à natureza da PriorityQueue. Em seguida, removemos e exibimos os elementos da fila de prioridade usando o método poll(), que remove e retorna o elemento com a maior prioridade (o menor valor neste caso). A saída será:
10
20
30
40
Isso mostra que os elementos foram removidos em ordem ascendente, conforme sua prioridade na PriorityQueue.

ArrayDeque:

ArrayDeque é uma implementação da interface Deque que armazena elementos em um array dinâmico, ou seja, um deque redimensionável. Ele não é sincronizado e não permite elementos nulos. O ArrayDeque é mais eficiente em termos de espaço e tempo do que o LinkedList para adição e remoção de elementos, especialmente no início e no final da deque.

Exemplo de Aplicação:
//Exemplo:
import java.util.ArrayDeque;


public class ExemploArrayDeque {
  public static void main(String[] args) {
      // Criando um ArrayDeque de Integers
      ArrayDeque<Integer> deque = new ArrayDeque<>();


      // Adicionando elementos ao deque
      deque.addFirst(10);
      deque.addLast(20);
      deque.addFirst(5);


      // Removendo e exibindo os elementos do deque
      while (!deque.isEmpty()) {
          System.out.println(deque.removeFirst());
      }
  }
}

Neste exemplo, criamos um ArrayDeque de Integers e adicionamos alguns números a ele. Utilizamos os métodos addFirst() e addLast() para adicionar elementos no início e no final da deque, respectivamente. Em seguida, removemos e exibimos os elementos do deque usando o método removeFirst(), que remove e retorna o primeiro elemento da deque. A saída será:
5
10
20
Isso mostra que os elementos foram removidos na ordem em que foram adicionados, seguindo o princípio FIFO (First-In-First-Out) para a deque.

Em conclusão, as interfaces List, Set, Map e Queue são componentes essenciais do framework de coleções do Java, cada uma com suas próprias características e aplicações específicas:

List: É uma coleção ordenada que permite elementos duplicados e oferece acesso aos elementos por meio de índices. É útil quando a ordem dos elementos é importante e quando você precisa acessar elementos por sua posição na lista.

Set: É uma coleção que não permite elementos duplicados e não garante a ordem dos elementos. É eficiente para verificar a presença de elementos e garantir a unicidade deles.

Map: É uma coleção que mapeia chaves únicas para valores correspondentes. É útil quando você precisa associar informações ou realizar pesquisas rápidas com base em chaves únicas.

Queue: É uma coleção ordenada usada para armazenar elementos antes de serem processados, seguindo o princípio FIFO (First-In-First-Out). É útil para implementar filas de tarefas, algoritmos de busca em largura (BFS) e simulações de eventos.

Cada uma dessas interfaces tem suas próprias implementações específicas, como ArrayList, HashSet, HashMap e LinkedList, que oferecem diferentes características e desempenhos. A escolha da implementação correta depende dos requisitos do seu projeto, como eficiência de acesso, necessidade de ordem ou unicidade de elementos, e requisitos de sincronização em ambientes concorrentes.

Share
Comments (1)
Victor Viana
Victor Viana - 17/04/2024 07:55

MUITO BOM