Neste artigo trabalharemos com alguns recursos disponíveis na linguagem Java, muito úteis e indispensáveis no dia a dia do profissional, são eles: List, ArrayList, Set, HashSet, Iterator e um pouco de Generics.

No decorrer do artigo você verá que ambos os recursos tem suas correlações, ou seja, os assuntos abordados estão ligados mesmo que indiretamente. Explicaremos o uso de cada um destes.

Antes de iniciar as explicações de cada um é importante realizar uma pequena comparação entre os seguintes recursos: List e Set. Ambos armazenam uma “lista”. A diferença principal é que o 'Set' não aceita elementos duplicados, diferente do List.

List e ArrayList

O List é uma interface e o ArrayList é a classe que a implementa. Veja um exemplo na Listagem 1.

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
 
 
public class DevmediaList {
 
       /**
        * @param args
        */
       public static void main(String[] args) {
             
             /*
              * Declaremos nosso objeto sem nenhuma instância
              *
              * */
             List carros;
             
             /*
              * Criamos uma instância qualquer para o nosso objeto
              * do tipo List, sendo que essa instância deve 
               * obrigatoriamente implementar a interface List.
              * Veja as possibilidades
              * */
             carros = new ArrayList();
             carros = new LinkedList();
             
             //e assim por diante...
             
 
       }
 
}
Listagem 1. Criando um ArrayList a partir do List

Então você pode questionar: Porque não fazer um ArrayList carros = new ArrayList(); sem usar as interfaces? Por uma questão muito simples: você precisa garantir a flexibilidade da sua aplicação, garantir que o mesmo objeto possa ser instanciado de maneiras distintas em pontos distintos da aplicação. Como você tem uma interface List, você tem certeza dos métodos que seu objeto tem, mesmo que ele seja instanciado por uma classe como ArrayList ou LinkedList. Vamos melhorar um pouco mais nossa Listagem 1, adicionando o uso de Generics (Listagem 2).

import java.util.ArrayList;
import java.util.List;


public class DevmediaList {

     /**
      * @param args
      */
     public static void main(String[] args) {
           
           /*
            * Agora adicionamos um Generic "<Carro>", assim só podemos
            * adicionar objetos do tipo 'Carro' nossa lista.
            *
            * */
           List<Carro> carros;
           
           /*
            * Criamos uma ArrayList apenas com carros
            * */
           carros = new ArrayList<Carro>();
           
           
           /*
            * Você conseguirá criar sem problemas um ArrayList 
            * sem passar o tipo Generic, mas a IDE irá dar um
             * Warning dizendo que você deveria passar um
            * tipo Generic, para evitar erros em runtime.
            * */        
           carros = new ArrayList();        
           
           /*
            * O código abaixo não será compilado, erro em tempo de 
            * design. Pois nosso objeto 'carros' agora é muito 
             * exigente: Só aceita implementações de List que tenham
            * objetos do tipo 'Carro'. Estamos tentando fazer 
             * um 'cast' de Animal para Carro e
            * isso não possível.
            * */
           carros = new ArrayList<Animal>();
           
     }

}
Listagem 2. Usando Generics com ArrayList e List

Para finalizar o uso do List, vamos mostrar alguns métodos muito utilizados no dia a dia. Observe a Listagem 3.

import java.util.ArrayList;
import java.util.List;
 
 
public class DevmediaList {

   /**
    * @param args
    */
   public static void main(String[] args) {           
         
         List<Carro> carros = new ArrayList<Carro>();
         List<Carro> carrosNovos = new ArrayList<Carro>();
         
         Carro carro = new Carro();
         
         //adiciona um carro a nossa lista
         carros.add(carro);
         
         //adiciona um carro a uma posição exata da lista
         carros.add(10, carro);
         
         /*
          * Imagine que desejamos adicionar uma lista de 
           * carros novos a nossa lista de carros,
          * podemos adicionar todos de uma só vez, usando o 'addAll'
          * */
         carros.addAll(carrosNovos);
         
         /* Caso prefira, pode usar um for para adicionar 
          * os carrosNovos.Nosso laço FOR abaixo, tem a mesma 
           * função do addAll.
         */
         for(int i = 0 ; i < carrosNovos.size(); i++){
                carros.add(carrosNovos.get(i));
         }
         
         /*
          * Verifica se o carro está na lista de carros. 
           * Mas como essa verificação é feita ?
          * Através do método 'equals' do seu objeto carro. 
           * Sendo assim, na implementação do
          * seu bean 'Carro', é muito importante que você 
           * sobreescreva o método 'equals' dizendo
          * como as comparações devem ser realizadas.
          * */
         carros.contains(carro);
         
         //Remove todos os elementos da lista
         carros.clear();     

   }

}
Listagem 3. Uso de métodos frequentes com List

Esses métodos mencionados acima são os usados com muita frequência, mas nada impede que você faça um estudo em cada método.

Set e HashSet

Assim como o List é uma interface e o ArrayList é sua implementação, o Set e HashSet seguem a mesma analogia, onde o Set é a interface e o HashSet é sua implementação. Observe a Listagem 4.

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;


public class DevmediaSet {

   /**
    * @param args
    */
   public static void main(String[] args) {
         
         Set<Carro> carros;
         
         //Você pode implementar com HashSet
         carros = new HashSet<Carro>();
   
         
         //Pode também optar pelo LinkedHashSet
         carros = new LinkedHashSet<Carro>();
         
         //e assim por diante       
         

   }

}
Listagem 4. Usando Set

Você já deve ter percebido que podemos implementar diferentes instâncias para uma mesma interface Set, obviamente que cada uma tem sua peculiaridade e não é foco deste artigo mostrar as peculiaridades de cada implementação.

Uma diferença muito comum entre o Set e o List é o retorno de determinados elementos em uma posição exata da lista, pois com o List podemos simplesmente fazer um 'get(index)' e você terá o elemento X na posição INDEX. Na Listagem 5 iremos demonstrar como percorrer todos só elementos usando o iterator.

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
 
 
public class DevmediaSet {

   /**
    * @param args
    */
   public static void main(String[] args) {
         
         Set<Carro> carros;
         
         //Você pode implementar com HashSet
         carros = new HashSet<Carro>();
   
         /*
          * Percorrer todo SET com iterator
          * */
         Iterator<Carro> carrosAsIterator = carros.iterator();
         while (carrosAsIterator.hasNext()){
                Carro it = carrosAsIterator.next();
                
         }
         

   }

}
Listagem 5. Usando iterator para capturar elementos no Set

No exemplo acima, abstraíamos a maioria dos comentários que já fizemos nas listagens anteriores, assim tornamos o mesmo sucinto e prático. Veja na Listagem 6 um exemplo mais completo de como trabalhar com Iterator.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

public class DevmediaSet2 {

 public static void main(String args[]) {

    ArrayList al = new ArrayList();

    al.add("C");
    al.add("A");
    al.add("E");
    al.add("B");
    al.add("D");
    al.add("F");

    // Usamos o iterator para mostrar o conteúdo do ArrayList
    System.out.print("Original contents of al: ");
    Iterator itr = al.iterator();
    while(itr.hasNext()) {
       Object element = itr.next();
       System.out.print(element + " ");
    }
    System.out.println();
    
       // Modify objects being iterated
    ListIterator litr = al.listIterator();
    while(litr.hasNext()) {
       Object element = litr.next();
       litr.set(element + "+");
    }
    System.out.print("Modified contents of al: ");
    itr = al.iterator();
    while(itr.hasNext()) {
       Object element = itr.next();
       System.out.print(element + " ");
    }
    System.out.println();


    System.out.print("Modified list backwards: ");
    while(litr.hasPrevious()) {
       Object element = litr.previous();
       System.out.print(element + " ");
     }
     System.out.println();
  }
}
Listagem 6. Trabalhando com Iterator

Os exemplos mostrados acima são práticos o suficiente para que você leitor possa questionar o uso de tais recursos em seu dia a dia. Algumas vezes utilizados de forma errada. É comum, por exemplo, em projetos iniciantes notarmos a falta de interfaces e implementações corretas de polimorfismo, por exemplo. Este artigo tem como principal objeto não só mostrar o uso dos recursos, já conhecidos por muitos, mas também mostrar a forma correta e ideal de utilizá-los. Então, caso você esteja utilizando outra forma que seja distinta da mostrada nas seções anteriores, aconselhamos que a mesma seja analisada.

Dizemos 'analisada', pois toda nova solução ou alteração deve ser analisada antes de alterada, e não simplesmente trocada sem uma análise crítica.

Para finalizar veja na Listagem 7 uma forma alternativa de utilizar o iterator com um 'foreach'.

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;


public class DevmediaSet {

   /**
    * @param args
    */
   public static void main(String[] args) {
         
         Set<Carro> carros;
         
         //Você pode implementar com HashSet
         carros = new HashSet<Carro>();
   
         /*
          * Percorrer todo SET com iterator usnado foreach
          * */
         for(Iterator<Carro> iter = carros.iterator(); 
           iter.hasNext();) {
             Carro carroAtual = iter.next();
         }
         

   }

}
Listagem 7. Usando iterator com foreach

A forma demonstrada acima é uma alternativa ao while(it.hasNext()) que utilizamos nas listagens acima.

Com isso, o artigo aqui apresentado teve foco em maior parte do tempo na prática voltada aos recursos que estudados, deixando um pouco a teoria de lado. Mas vale ressaltar que estudar os recursos (suas classes, sendo mais específico) é muito importante para entender o funcionamento como um todo, e não se ater somente ao descrito neste artigo.