Qualquer grupo de objetos individuais representados como uma única unidade é conhecido como a coleção dos objetos. Em Java, um framework separado denominado “Collection Framework” foi definido no JDK 1.2 que contém todas as classes de coleção e interface nele.

A interface Collection ( java.util.Collection ) e a interface Map ( java.util.Map ) são as duas interfaces “raiz” principais das classes de coleção Java.

O que é um Framework?

Um framework é um conjunto de classes e interfaces que fornecem uma arquitetura pronta. Para implementar um novo recurso ou classe, não há necessidade de definir um framework. No entanto, um projeto orientado a objetos ideal sempre inclui uma estrutura com uma coleção de classes de forma que todas as classes executem o mesmo tipo de tarefa.

Necessidade de uma estrutura de coleta separada

Antes da coleta Framework (ou antes JDK 1.2) foi introduzida, os métodos padrão para agrupar objetos Java (ou coleções) foram Matrizes ou vetores ou Hashtables . Todas essas coleções não tinham uma interface comum. Portanto, embora o objetivo principal de todas as coleções seja o mesmo, a implementação de todas essas coleções foi definida de forma independente e não teve correlação entre elas. E também, é muito difícil para os usuários lembrar de todos os diferentes métodos , sintaxe e construtores presentes em cada classe de coleção.

Vamos entender isso com um exemplo de adição de um elemento em uma hashtable e um vetor.

// Java program to demonstrate
// why collection framework was needed
import java.io.*;
import java.util.*;
  
class CollectionDemo {
  
    public static void main(String[] args)
    {
        // Creating instances of the array,
        // vector and hashtable
        int arr[] = new int[] { 1, 2, 3, 4 };
        Vector<Integer> v = new Vector();
        Hashtable<Integer, String> h
            = new Hashtable();
  
        // Adding the elements into the
        // vector
        v.addElement(1);
        v.addElement(2);
  
        // Adding the element into the
        // hashtable
        h.put(1, "geeks");
        h.put(2, "4geeks");
  
        // Array instance creation requires [],
        // while Vector and hastable require ()
        // Vector element insertion requires addElement(),
        // but hashtable element insertion requires put()
  
        // Accessing the first element of the
        // array, vector and hashtable
        System.out.println(arr[0]);
        System.out.println(v.elementAt(0));
        System.out.println(h.get(1));
  
        // Array elements are accessed using [],
        // vector elements using elementAt()
        // and hashtable elements using get()
    }
}

Saída:

1
1
geeks

Como podemos observar, nenhuma dessas coleções (Array, Vector ou Hashtable) implementa uma interface de acesso de membro padrão, era muito difícil para os programadores escrever algoritmos que funcionassem para todos os tipos de coleções. Outra desvantagem é que a maioria dos métodos 'Vector' são finais, o que significa que não podemos estender a classe 'Vector' para implementar um tipo semelhante de coleção. Portanto, os desenvolvedores Java decidiram criar uma interface comum para lidar com os problemas mencionados acima e introduziram o Collection Framework na postagem do JDK 1.2, em que ambos, vetores legados e tabelas de hash, foram modificados para estar em conformidade com o Collection Framework.

Vantagens da estrutura de coleta: Como a falta de uma estrutura de coleta deu origem ao conjunto de desvantagens acima, as seguintes são as vantagens da estrutura de coleta.

  1. API consistente: a API tem um conjunto básico de interfaces como Collection , Set , List ou Map , todas as classes ( ArrayList , LinkedList , Vector, etc) que implementam essas interfaces têm algum conjunto comum de métodos.
  2. Reduz o esforço de programação: um programador não precisa se preocupar com o design da coleção, mas pode se concentrar em seu melhor uso em seu programa. Portanto, o conceito básico de abstração de programação orientada a objetos (isto é) foi implementado com sucesso.
  3. Aumenta a velocidade e a qualidade do programa: Aumenta o desempenho ao fornecer implementações de alto desempenho de algoritmos e estruturas de dados úteis porque, neste caso, o programador não precisa pensar na melhor implementação de uma estrutura de dados específica. Ele pode simplesmente usar a melhor implementação para aumentar drasticamente o desempenho de seu algoritmo / programa.

Hierarquia da estrutura da coleção

O pacote do utilitário (java.util) contém todas as classes e interfaces exigidas pela estrutura de coleta. A estrutura da coleção contém uma interface denominada interface iterável que fornece o iterador para iterar por todas as coleções. Essa interface é estendida pela interface de coleção principal, que atua como uma raiz para a estrutura de coleção. Todas as coleções estendem essa interface de coleção, estendendo assim as propriedades do iterador e os métodos dessa interface. A figura a seguir ilustra a hierarquia da estrutura de coleção.

Java-Collections-Framework-Hierarchy

Antes de entender os diferentes componentes da estrutura acima, vamos primeiro entender uma classe e uma interface.

  • Classe : uma classe é um projeto ou protótipo definido pelo usuário a partir do qual os objetos são criados. Ele representa o conjunto de propriedades ou métodos comuns a todos os objetos de um tipo.
  • Interface : como uma classe, uma interface pode ter métodos e variáveis, mas os métodos declarados em uma interface são, por padrão, abstratos (apenas assinatura de método, sem corpo). As interfaces especificam o que uma classe deve fazer e não como. É o projeto da classe.

Métodos da interface de coleção

Esta interface contém vários métodos que podem ser usados ​​diretamente por todas as coleções que implementam esta interface. Eles são:

<style>.collections-methods-table{border-collapse:collapse;width:100%}.collections-methods-table td{border:1px solid #5fb962;text-align:left!important;padding:8px}.collections-methods-table th{border:1px solid #5fb962;padding:8px}.collections-methods-table tr>th{background-color:#c6ebd9;vertical-align:middle}.collections-methods-table tr:nth-child(odd){background-color:#fff}</style>
MétodoDescrição
adicionar (objeto)Este método é usado para adicionar um objeto à coleção.
addAll (coleção c)Este método adiciona todos os elementos da coleção fornecida a esta coleção.
Claro()Este método remove todos os elementos desta coleção.
contém (objeto o)Este método retorna verdadeiro se a coleção contém o elemento especificado.
containsAll (coleção c)Este método retorna true se a coleção contém todos os elementos na coleção fornecida.
é igual a (objeto o)Este método compara o objeto especificado com esta coleção de igualdade.
hashCode()Este método é usado para retornar o valor do código hash para esta coleção.
está vazia()Este método retorna verdadeiro se esta coleção não contém elementos.
iterador()Este método retorna um iterador sobre os elementos nesta coleção.
max()Este método é usado para retornar o valor máximo presente na coleção.
parallelStream()Este método retorna um Stream paralelo com esta coleção como sua fonte.
remover (objeto o)Este método é usado para remover o objeto fornecido da coleção. Se houver valores duplicados, este método remove a primeira ocorrência do objeto.
removeAll (coleção c)Este método é usado para remover todos os objetos mencionados na coleção fornecida da coleção.
removeIf (filtro de predicado)Este método é usado para remover todos os elementos desta coleção que satisfaçam o predicado fornecido .
reter tudo (coleção c)Este método é usado para reter apenas os elementos nesta coleção que estão contidos na coleção especificada.
Tamanho()Este método é usado para retornar o número de elementos da coleção.
divisor()Este método é usado para criar um Divisor sobre os elementos desta coleção.
Stream()Este método é usado para retornar um Stream sequencial com esta coleção como sua fonte.
toArray()Este método é usado para retornar uma matriz contendo todos os elementos desta coleção.

Interfaces que estendem a interface de coleções

A estrutura de coleta contém várias interfaces em que cada interface é usada para armazenar um tipo específico de dados. A seguir estão as interfaces presentes no framework.

1. Interface Iterável: Esta é a interface raiz de toda a estrutura de coleção. A interface de coleção estende a interface iterável. Portanto, inerentemente, todas as interfaces e classes implementam essa interface. A principal funcionalidade dessa interface é fornecer um iterador para as coleções. Portanto, essa interface contém apenas um método abstrato que é o iterador. Retorna o

Iterator <t>iterador();</t>

2. Interface de coleção: esta interface estende a interface iterável e é implementada por todas as classes na estrutura de coleção. Esta interface contém todos os métodos básicos que cada coleção possui, como adicionar os dados na coleção, remover os dados, limpar os dados, etc. Todos esses métodos são implementados nesta interface porque esses métodos são implementados por todas as classes, independentemente de seu estilo de implementação. E também, ter esses métodos nesta interface garante que os nomes dos métodos sejam universais para todas as coleções. Portanto, em resumo, podemos dizer que essa interface constrói uma base sobre a qual as classes de coleção são implementadas.

3. Interface de lista: esta é uma interface filha da interface de coleção. Esta interface é dedicada aos dados do tipo lista em que podemos armazenar toda a coleção ordenada dos objetos. Isso também permite que dados duplicados estejam presentes nele. Essa interface de lista é implementada por várias classes como ArrayList , Vector , Stack , etc. Como todas as subclasses implementam a lista, podemos instanciar um objeto de lista com qualquer uma dessas classes. Por exemplo,

List <T> al = new ArrayList <>();
Lista <T> ll = nova LinkedList <>();
Lista <T> v = novo vetor <>();
Onde T é o tipo de objeto

As classes que implementam a interface List são as seguintes:

  • ArrayList: ArrayList nos fornece matrizes dinâmicas em Java. Porém, pode ser mais lento do que os arrays padrão, mas pode ser útil em programas onde é necessária muita manipulação no array. O tamanho de um ArrayList é aumentado automaticamente se a coleção aumentar ou diminuir se os objetos forem removidos da coleção. Java ArrayList nos permite acessar aleatoriamente a lista. ArrayList não pode ser usado para tipos primitivos , como int, char, etc. Precisamos de uma classe wrapper para tais casos. Vamos entender o arraylist com o seguinte exemplo:




    // Java program to demonstrate the
    // working of ArrayList in Java
      
    import java.io.*;
    import java.util.*;
      
    class GFG {
        public static void main(String[] args)
        {
      
            // Declaring the ArrayList with
            // initial size n
            ArrayList<Integer> al
                = new ArrayList<Integer>();
      
            // Appending new elements at
            // the end of the list
            for (int i = 1; i <= 5; i++)
                al.add(i);
      
            // Printing elements
            System.out.println(al);
      
            // Remove element at index 3
            al.remove(3);
      
            // Displaying the ArrayList
            // after deletion
            System.out.println(al);
      
            // Printing elements one by one
            for (int i = 0; i < al.size(); i++)
                System.out.print(al.get(i) + " ");
        }
    }
    Saída:

    [1, 2, 3, 4, 5]
    [1, 2, 3, 5]
    1 2 3 5
    
  • LinkedList: a classe LinkedList é uma implementação da estrutura de dados LinkedList, que é uma estrutura de dados linear onde os elementos não são armazenados em locais contíguos e cada elemento é um objeto separado com uma parte de dados e uma parte de endereço. Os elementos são vinculados por meio de ponteiros e endereços. Cada elemento é conhecido como um nó. Vamos entender a LinekdList com o seguinte exemplo:




    // Java program to demonstrate the
    // working of LinkedList in Java
      
    import java.io.*;
    import java.util.*;
      
    class GFG {
        public static void main(String[] args)
        {
      
            // Declaring the LinkedList
            LinkedList<Integer> ll
                = new LinkedList<Integer>();
      
            // Appending new elements at
            // the end of the list
            for (int i = 1; i <= 5; i++)
                ll.add(i);
      
            // Printing elements
            System.out.println(ll);
      
            // Remove element at index 3
            ll.remove(3);
      
            // Displaying the List
            // after deletion
            System.out.println(ll);
      
            // Printing elements one by one
            for (int i = 0; i < ll.size(); i++)
                System.out.print(ll.get(i) + " ");
        }
    }
    Saída:
    [1, 2, 3, 4, 5]
    [1, 2, 3, 5]
    1 2 3 5
    
  • Vector: Um vetor nos fornece matrizes dinâmicas em Java. Porém, pode ser mais lento do que os arrays padrão, mas pode ser útil em programas onde é necessária muita manipulação no array. Isso é idêntico ao ArrayList em termos de implementação. No entanto, a principal diferença entre um vetor e uma ArrayList é que um Vector é sincronizado e uma ArrayList não está sincronizada. Vamos entender o vetor com um exemplo:




    // Java program to demonstrate the
    // working of Vector in Java
      
    import java.io.*;
    import java.util.*;
      
    class GFG {
        public static void main(String[] args)
        {
      
            // Declaring the Vector
            Vector<Integer> v
                = new Vector<Integer>();
      
            // Appending new elements at
            // the end of the list
            for (int i = 1; i <= 5; i++)
                v.add(i);
      
            // Printing elements
            System.out.println(v);
      
            // Remove element at index 3
            v.remove(3);
      
            // Displaying the Vector
            // after deletion
            System.out.println(v);
      
            // Printing elements one by one
            for (int i = 0; i < v.size(); i++)
                System.out.print(v.get(i) + " ");
        }
    }
    Saída:
    [1, 2, 3, 4, 5]
    [1, 2, 3, 5]
    1 2 3 5
    
  • Stack : a classe Stack modela e implementa a estrutura de dados Stack . A aula é baseada no princípio básico do último a entrar, primeiro a sair . Além das operações básicas de push e pop, a classe oferece mais três funções de empty, search e peek. A classe também pode ser chamada de subclasse de Vector. Vamos entender a pilha com um exemplo:




    // Java program to demonstrate the
    // working of a stack
      
    import java.util.*;
    public class GFG {
        public static void main(String args[])
        {
            Stack<String> stack = new Stack<String>();
            stack.push("Geeks");
            stack.push("For");
            stack.push("Geeks");
            stack.push("Geeks");
      
            // Iterator for the stack
            Iterator<String> itr
                = stack.iterator();
      
            // Printing the stack
            while (itr.hasNext()) {
                System.out.print(itr.next() + " ");
            }
      
            System.out.println();
      
            stack.pop();
      
            // Iterator for the stack
            itr
                = stack.iterator();
      
            // Printing the stack
            while (itr.hasNext()) {
                System.out.print(itr.next() + " ");
            }
        }
    }
    Saída:
    Geeks para Geeks Geeks 
    Geeks para Geeks
    


    Nota:
    Stack é uma subclasse de Vector e uma classe legada. É thread-safe, o que pode ser uma sobrecarga em um ambiente onde a thread safety não é necessária. Uma alternativa para Stack é usar ArrayDequeue, que não é thread-safe e uma implementação de array mais rápida.

4. Interface de fila : Como o nome sugere, uma interface de fila mantém a ordem FIFO (primeiro a entrar, primeiro a sair) semelhante a uma linha de fila do mundo real. Esta interface é dedicada a armazenar todos os elementos onde a ordem dos elementos importa. Por exemplo, sempre que tentamos reservar um ingresso, os ingressos são vendidos por ordem de chegada. Portanto, a pessoa cuja solicitação chega primeiro na fila obtém o tíquete. Existem várias classes como PriorityQueue , Deque , ArrayDeque , etc. Como todas essas subclasses implementam a fila, podemos instanciar um objeto de fila com qualquer uma dessas classes. Por exemplo,

Queue <T> pq = new PriorityQueue <>();
Queue <T> ad = new ArrayDeque <>();
Onde T é o tipo do objeto.

A implementação mais freqüentemente usada da interface de fila é a PriorityQueue.

  • Fila de prioridade: uma PriorityQueue é usada quando os objetos devem ser processados ​​com base na prioridade. Sabe-se que uma fila segue o algoritmo First-In-First-Out, mas às vezes os elementos da fila precisam ser processados ​​de acordo com a prioridade e essa classe é usada nesses casos. O PriorityQueue é baseado no heap de prioridade. Os elementos da fila de prioridade são ordenados de acordo com a ordem natural ou por um Comparador fornecido no momento da construção da fila, dependendo de qual construtor é usado. Vamos entender a fila de prioridade com um exemplo:




    // Java program to demonstrate the working of
    // priority queue in Java
    import java.util.*;
      
    class GfG {
        public static void main(String args[])
        {
            // Creating empty priority queue
            PriorityQueue<Integer> pQueue
                = new PriorityQueue<Integer>();
      
            // Adding items to the pQueue using add()
            pQueue.add(10);
            pQueue.add(20);
            pQueue.add(15);
      
            // Printing the top element of PriorityQueue
            System.out.println(pQueue.peek());
      
            // Printing the top element and removing it
            // from the PriorityQueue container
            System.out.println(pQueue.poll());
      
            // Printing the top element again
            System.out.println(pQueue.peek());
        }
    }
    Saída:
    10
    10
    15
    

5. Interface Deque : Esta é uma variação muito pequena da estrutura de dados da fila . Deque , também conhecido como fila de extremidade dupla, é uma estrutura de dados onde podemos adicionar e remover os elementos de ambas as extremidades da fila. Essa interface estende a interface da fila. A classe que implementa essa interface é ArrayDeque . Como essa classe implementa o deque, podemos instanciar um objeto deque com essa classe. Por exemplo,

Deque <T> ad = new ArrayDeque <>();
Onde T é o tipo do objeto.



A classe que implementa a interface deque é ArrayDeque.

  • ArrayDeque: a classe ArrayDeque implementada na estrutura da coleção nos fornece uma maneira de aplicar o array redimensionável. Este é um tipo especial de array que cresce e permite aos usuários adicionar ou remover um elemento de ambos os lados da fila. Os array deques não têm restrições de capacidade e crescem conforme necessário para oferecer suporte ao uso. Vamos entender o ArrayDeque com um exemplo:




    // Java program to demonstrate the
    // ArrayDeque class in Java
      
    import java.util.*;
    public class ArrayDequeDemo {
        public static void main(String[] args)
        {
            // Initializing an deque
            ArrayDeque<Integer> de_que
                = new ArrayDeque<Integer>(10);
      
            // add() method to insert
            de_que.add(10);
            de_que.add(20);
            de_que.add(30);
            de_que.add(40);
            de_que.add(50);
      
            System.out.println(de_que);
      
            // clear() method
            de_que.clear();
      
            // addFirst() method to insert the
            // elements at the head
            de_que.addFirst(564);
            de_que.addFirst(291);
      
            // addLast() method to insert the
            // elements at the tail
            de_que.addLast(24);
            de_que.addLast(14);
      
            System.out.println(de_que);
        }
    }
    Saída:
    [10, 20, 30, 40, 50]
    [291, 564, 24, 14]
    

6. Interface do conjunto : Um conjunto é uma coleção não ordenada de objetos nos quais valores duplicados não podem ser armazenados. Esta coleção é usada quando desejamos evitar a duplicação dos objetos e desejamos armazenar apenas os objetos únicos. Essa interface de conjunto é implementada por várias classes como HashSet , TreeSet , LinkedHashSet , etc. Uma vez que todas as subclasses implementam o conjunto, podemos instanciar um objeto de conjunto com qualquer uma dessas classes. Por exemplo,

Defina <T> hs = new HashSet <>();
Defina <T> lhs = new LinkedHashSet <>();
Defina <T> ts = novo TreeSet <>();
Onde T é o tipo do objeto.

A seguir estão as classes que implementam a interface Set:

  • HashSet: a classe HashSet é uma implementação inerente da estrutura de dados da tabela de hash. Os objetos que inserimos no HashSet não garantem que sejam inseridos na mesma ordem. Os objetos são inseridos com base em seu hashcode. Esta classe também permite a inserção de elementos NULL. Vamos entender o HashSet com um exemplo:




    // Java program to demonstrate the
    // working of a HashSet
      
    import java.util.*;
    public class HashSetDemo {
        public static void main(String args[])
        {
            // Creating HashSet and
            // adding elements
            HashSet<String> hs = new HashSet<String>();
      
            hs.add("Geeks");
            hs.add("For");
            hs.add("Geeks");
            hs.add("Is");
            hs.add("Very helpful");
      
            // Traversing elements
            Iterator<String> itr = hs.iterator();
            while (itr.hasNext()) {
                System.out.println(itr.next());
            }
        }
    }
    Saída:
    Muito útil
    Geeks
    Para
    É
    
  • LinkedHashSet : Um LinkedHashSet é muito semelhante a um HashSet. A diferença é que ele usa uma lista duplamente vinculada para armazenar os dados e retém a ordem dos elementos. Vamos entender o LinkedHashSet com um exemplo:




    // Java program to demonstrate the
    // working of a LinkedHashSet
      
    import java.util.*;
    public class LinkedHashSetDemo {
        public static void main(String args[])
        {
            // Creating LinkedHashSet and
            // adding elements
            LinkedHashSet<String> lhs
                = new LinkedHashSet<String>();
      
            lhs.add("Geeks");
            lhs.add("For");
            lhs.add("Geeks");
            lhs.add("Is");
            lhs.add("Very helpful");
      
            // Traversing elements
            Iterator<String> itr = lhs.iterator();
            while (itr.hasNext()) {
                System.out.println(itr.next());
            }
        }
    }
    Saída:
    Geeks
    Para
    É
    Muito útil
    

7. Interface do conjunto ordenado : Esta interface é muito semelhante à interface do conjunto. A única diferença é que essa interface possui métodos extras que mantêm a ordem dos elementos. A interface do conjunto classificado estende a interface do conjunto e é usada para lidar com os dados que precisam ser classificados. A classe que implementa essa interface é TreeSet. Como essa classe implementa o SortedSet, podemos instanciar um objeto SortedSet com essa classe. Por exemplo,

SortedSet <T> ts = new TreeSet <>();
Onde T é o tipo do objeto.

A classe que implementa a interface de conjunto classificado é TreeSet.

  • TreeSet: a classe TreeSet usa uma árvore para armazenamento. A ordem dos elementos é mantida por um conjunto usando sua ordem natural, quer um comparador explícito seja fornecido ou não. Isso deve ser consistente com igual para implementar corretamente a interface Set. Ele também pode ser solicitado por um Comparador fornecido no momento da criação do conjunto, dependendo de qual construtor é usado. Vamos entender o TreeSet com um exemplo:




    // Java program to demonstrate the
    // working of a TreeSet
      
    import java.util.*;
    public class TreeSetDemo {
        public static void main(String args[])
        {
            // Creating TreeSet and
            // adding elements
            TreeSet<String> ts
                = new TreeSet<String>();
      
            ts.add("Geeks");
            ts.add("For");
            ts.add("Geeks");
            ts.add("Is");
            ts.add("Very helpful");
      
            // Traversing elements
            Iterator<String> itr = ts.iterator();
            while (itr.hasNext()) {
                System.out.println(itr.next());
            }
        }
    }
    Saída:
    Para
    Geeks
    É
    Muito útil
    

8. Interface de mapa : Um mapa é uma estrutura de dados que suporta o mapeamento de par de valores-chave para os dados. Esta interface não oferece suporte a chaves duplicadas porque a mesma chave não pode ter vários mapeamentos. Um mapa é útil se houver dados e desejarmos realizar operações com base na chave. Essa interface de mapa é implementada por várias classes, como HashMap , TreeMap etc. Como todas as subclasses implementam o mapa, podemos instanciar um objeto de mapa com qualquer uma dessas classes. Por exemplo,

Map <T> hm = new HashMap <>();
Map <T> tm = novo TreeMap <>();
Onde T é o tipo do objeto.

A implementação freqüentemente usada de uma interface de mapa é um HashMap.

  • HashMap : HashMap fornece a implementação básica da interface de mapa de Java. Ele armazena os dados em pares (chave, valor). Para acessar um valor em um HashMap, devemos conhecer sua chave. HashMap usa uma técnica chamada Hashing. Hashing é uma técnica de conversão de uma string grande em uma string pequena que representa a mesma string para que as operações de indexação e pesquisa sejam mais rápidas. O HashSet também usa o HashMap internamente. Vamos entender o HashMap com um exemplo:




    // Java program to demonstrate the
    // working of a HashMap
      
    import java.util.*;
    public class HashMapDemo {
        public static void main(String args[])
        {
            // Creating HashMap and
            // adding elements
            HashMap<Integer, String> hm
                = new HashMap<Integer, String>();
      
            hm.put(1, "Geeks");
            hm.put(2, "For");
            hm.put(3, "Geeks");
      
            // Finding the value for a key
            System.out.println("Value for 1 is " + hm.get(1));
      
            // Traversing through the HashMap
            for (Map.Entry<Integer, String> e : hm.entrySet())
                System.out.println(e.getKey() + " " + e.getValue());
        }
    }
    Saída:
    Valor para 1 é Geeks
    1 geeks
    2 para
    3 geeks
    

O que você deve aprender nas coleções Java?