sexta-feira, 7 de outubro de 2016

Aprendendo Collections Framework

Para entender como as principais estruturas de dados já implementadas na API Java funcionam, escrevi o seguinte código:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

public class Colecoes {
    //Método que remove strings duplicadas de uma Lista de Strings
    public static List<String> removeDuplicates(List<String> lista) {
        List<String> listaCopy = new ArrayList<String>();
        List<String> posicoesCopy = new ArrayList<String>();
       
        for(int i = 0; i < lista.size(); i++) {
            listaCopy.add(lista.get(i));
        }
       
        for(int i = 0; i < lista.size(); i++) {
            for(int j = 0; j < listaCopy.size(); j++) {
                if(lista.get(i).equals(listaCopy.get(j)) && j > i) posicoesCopy.add(String.valueOf(j));
            }
        }
       
        for(int i = 0; i < posicoesCopy.size(); i++) {
            lista.remove(Integer.parseInt(posicoesCopy.get(i)));
        }
       
        return lista;
    }

    public static void main(String[] args) {
        List<String> frutaArrayList = new ArrayList<String>();
        Set<String> frutaTreeSet = new TreeSet<String>();
        HashMap<String, String> frutaHashMap = new HashMap<String, String>();
        List<String> frutaLinkedList = new LinkedList<String>();
        Set<String> frutaHashSet = new HashSet<String>();
        LinkedHashMap<String, String> frutaLinkedHashMap = new LinkedHashMap<String, String>();
       
        String[] frutas = {"banana", "morango", "abacaxi", "damasco",
                "abacaxi", "banana", "abacate"
        };
       
        for(int i = 0; i < frutas.length; i++) {
            frutaArrayList.add(frutas[i]);
            frutaTreeSet.add(frutas[i]);
            frutaHashMap.put(String.valueOf(i), frutas[i]);
            frutaLinkedList.add(frutas[i]);
            frutaHashSet.add(frutas[i]);
            frutaLinkedHashMap.put(String.valueOf(i), frutas[i]);
        }
       
        System.out.println("ArrayList: "+frutaArrayList);
        System.out.println("TreeSet: "+frutaTreeSet);
        System.out.println("HashMap: "+frutaHashMap);
        System.out.println("LinkedList: "+frutaLinkedList);
        System.out.println("HashSet: "+frutaHashSet);
        System.out.println("LinkedHashMap: "+frutaLinkedHashMap);
       
        System.out.println("\nRemovendo duplicadas da ArrayList: ");
        System.out.println(removeDuplicates(frutaArrayList));
    }
}

Como resultado da execução do código acima, obtive a seguinte saída:

ArrayList: [banana, morango, abacaxi, damasco, abacaxi, banana, abacate]
TreeSet: [abacate, abacaxi, banana, damasco, morango]
HashMap: {0=banana, 1=morango, 2=abacaxi, 3=damasco, 4=abacaxi, 5=banana, 6=abacate}
LinkedList: [banana, morango, abacaxi, damasco, abacaxi, banana, abacate]
HashSet: [banana, morango, abacaxi, abacate, damasco]
LinkedHashMap: {0=banana, 1=morango, 2=abacaxi, 3=damasco, 4=abacaxi, 5=banana, 6=abacate}

Removendo duplicadas da ArrayList:
[banana, morango, abacaxi, damasco, abacate]

Com isso, pude aprender que as estruturas do tipo 'Set' eliminam as palavras duplicadas automaticamente, sendo que a TreeSet, além de fazer isso, já põe em ordem alfabética. Também aprendi que não se pode percorrer uma estrutura do tipo 'Set', pois ela não possui um método get(). A HashSet trocou de posição as palavras 'abacate' e 'damasco', o que demonstra que estruturas do tipo 'Set' não respeitam a ordem de adição, organizando os objetos de qualquer maneira ou em ordem alfabética, no caso da TreeSet.

É possível ainda melhorar o método "removeDuplicates", pois a lista está sendo percorrida várias vezes desnecessariamente. Assim, se essa lista fosse muito grande, isso causaria lentidão no processamento. Uma maneira de melhoria pode ser vista logo abaixo:

public static List<String> removeDuplicates(List<String> lista) {
        List<String> result = new ArrayList<String>();
        Set<String> itemsSet = new HashSet<>();
   
        for(int i = 0; i < lista.size(); i++) {
            String s = lista.get(i);
            if (!itemsSet.contains(s)) {
                itemsSet.add(s);
                result.add(s);
            }
        }
        return result;
}

O segredo está no uso de um 'Set', no caso, o HashSet itemsSet, pois, com ele, não é preciso percorrer a lista para encontrar o que se quer. Ele vai diretamente ao ponto por meio de uma tabela hash e verifica com o método contains se existe ou não determinado elemento. Isso evita aquele aninhamento desnecessário de loops. Além disso, impede-se que elementos duplicados sejam adicionados a nova lista (result), pois a adição só ocorre se não existir o item analisado dentro de itemsSet. 

2 comentários: