CodeGym /Cursos /JAVA 25 SELF /LinkedHashSet/LinkedHashMap

LinkedHashSet/LinkedHashMap

JAVA 25 SELF
Nível 28 , Lição 4
Disponível

1. O que são LinkedHashSet e LinkedHashMap?

A biblioteca padrão do Java possui várias implementações de coleções Set e Map. As mais conhecidas são HashSet e HashMap. Essas estruturas fornecem acesso rápido aos elementos via hash, mas não garantem nenhuma ordem ao iterar sobre os elementos. Quando a ordem de iteração é importante, entram em cena LinkedHashSet e LinkedHashMap.

  • LinkedHashSet é um Set que lembra a ordem de adição dos elementos.
  • LinkedHashMap é um Map que lembra a ordem de inserção dos pares chave–valor (ou, opcionalmente, a ordem de acesso).

Elas são implementadas como HashSet/HashMap “comuns”, mas com um lista duplamente encadeada adicional para armazenar a ordem dos elementos.

2. Ordem de inserção e ordem de acesso

Ordem de inserção

Por padrão, LinkedHashSet e LinkedHashMap garantem que, ao iterar, os elementos são retornados na mesma ordem em que foram adicionados (for-each/iterador).

Set<String> set = new LinkedHashSet<>();
set.add("A");
set.add("B");
set.add("C");
for (String s : set) {
    System.out.println(s);
}
// Saída: A B C
Map<Integer, String> map = new LinkedHashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for (Integer key : map.keySet()) {
    System.out.println(key + " -> " + map.get(key));
}
// Saída: 1 -> one, 2 -> two, 3 -> three

Ordem de acesso (apenas para LinkedHashMap)

O LinkedHashMap possui um modo de ordenação por último acesso (access order). Se, ao criar o mapa, você passar true como terceiro parâmetro do construtor, então, a cada acesso a um elemento (get/put), ele é movido para o fim da lista.

Map<Integer, String> lruMap = new LinkedHashMap<>(16, 0.75f, true);
lruMap.put(1, "one");
lruMap.put(2, "two");
lruMap.put(3, "three");

lruMap.get(2); // Acesso à chave 2

for (Integer key : lruMap.keySet()) {
    System.out.println(key);
}
// Saída: 1 3 2 (2 é o último porque foi acessado)

Cache LRU via removeEldestEntry

O principal diferencial do LinkedHashMap é a implementação simples de um cache LRU (Least Recently Used, “menos recentemente usado”). Basta sobrescrever o método removeEldestEntry(Map.Entry<K,V> eldest): se ele retornar true, o elemento mais “antigo” é removido ao adicionar um novo.

class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int maxSize;

    public LRUCache(int maxSize) {
        super(maxSize, 0.75f, true); // true — ordem por acesso
        this.maxSize = maxSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxSize;
    }
}

// Uso:
LRUCache<Integer, String> cache = new LRUCache<>(3);
cache.put(1, "one");
cache.put(2, "two");
cache.put(3, "three");
cache.get(1); // Tornamos 1 o mais 'recente'
cache.put(4, "four"); // 2 será removido (o mais antigo)
System.out.println(cache.keySet()); // [3, 1, 4]

3. Custo de memória e desempenho: LinkedHashSet/LinkedHashMap vs HashSet/HashMap

Como funciona

Por dentro, é quase como um HashSet/HashMap comum, mas cada elemento também é armazenado em uma lista duplamente encadeada. Essa estrutura adicional “lembra” a ordem de inserção/acesso.

Custo de memória

Devido às referências prev/next em cada nó, é necessária mais memória. Para milhões de elementos isso é perceptível; para casos típicos, é um preço justificável pela ordem determinística.

Custo de desempenho

As operações básicas de adicionar/buscar/remover continuam amortizadas em O(1). A iteração é um pouco mais lenta por causa do percurso da lista, mas na maioria dos casos a diferença é mínima. E o cenário “remover o mais antigo” (LRU) é implementado com muita eficiência, pois o elemento “mais velho” está sempre na cabeça da lista.

Resumo: se a ordem é importante — escolha LinkedHashSet/LinkedHashMap. Se você precisa economizar memória e a ordem não importa — HashSet/HashMap são suficientes.

4. Cache, saída determinística, testes estáveis

Cache (LRU)

O LinkedHashMap é a base ideal para caches com limite de tamanho e remoção automática dos elementos “antigos”. Aplica-se em bibliotecas/frameworks, no trabalho com arquivos, imagens e resultados de computações.

Saída determinística

Para relatórios, exportações e serialização, onde é crítico ter uma representação previsível dos dados, use LinkedHashSet/LinkedHashMap. Isso é especialmente importante para testes — a ordem não “oscila” de uma execução para outra.

Testes estáveis

Em testes unitários, é comum comparar coleções esperadas com as obtidas. Se a ordem não é garantida, os testes podem ficar “flaky”. Com as implementações “linked”, a determinística garante estabilidade.

Exemplo: comparação entre HashMap e LinkedHashMap

Map<Integer, String> hashMap = new HashMap<>();
Map<Integer, String> linkedMap = new LinkedHashMap<>();

for (int i = 1; i <= 5; i++) {
    hashMap.put(i, "val" + i);
    linkedMap.put(i, "val" + i);
}

System.out.println(hashMap.keySet());   // A ordem pode ser qualquer uma!
System.out.println(linkedMap.keySet()); // Sempre 1, 2, 3, 4, 5

5. LinkedList vs ArrayDeque para filas

LinkedList

  • Implementa as interfaces List, Deque, Queue.
  • Lista duplamente encadeada: inserções/remoções rápidas no início e no fim.
  • Pode ser usada como fila (FIFO), pilha (LIFO) e fila dupla.

ArrayDeque

  • Implementa Deque, Queue (mas não List).
  • Baseada em array, expande-se automaticamente.
  • Geralmente mais rápida que LinkedList para operações de fila/pilha; menor overhead.
  • Não suporta elemento null.

Quando usar o quê?

  • Filas e pilhas — quase sempre ArrayDeque.
  • Muitas inserções/remoções no meio e você também precisa de lista — LinkedList.
  • Set/Map com ordemLinkedHashSet/LinkedHashMap.

Exemplo: fila de tarefas

Queue<String> queue = new ArrayDeque<>();
queue.add("task1");
queue.add("task2");
System.out.println(queue.poll()); // task1

Exemplo: pilha

Deque<String> stack = new ArrayDeque<>();
stack.push("first");
stack.push("second");
System.out.println(stack.pop()); // second

Conclusão:
— Para filas e pilhas — ArrayDeque.
— Para listas com muitas inserções no meio — LinkedList.
— Para Set/Map com ordem — LinkedHashSet/LinkedHashMap.

6. Erros comuns ao trabalhar com LinkedHashSet/LinkedHashMap

Erro nº 1: esperar que HashSet/HashMap preservem a ordem.
HashSet/HashMap não garantem ordem. Se ela for importante — use LinkedHashSet/LinkedHashMap.

Erro nº 2: usar LinkedHashMap para cache sem sobrescrever removeEldestEntry.
Para LRU é preciso sobrescrever removeEldestEntry, caso contrário o mapa crescerá sem limites.

Erro nº 3: usar LinkedList para filas/pilhas sem necessidade.
Na maioria dos casos, ArrayDeque é mais rápido e econômico.

Erro nº 4: esperar ordenação de LinkedHashMap.
LinkedHashMap preserva a ordem de inserção/acesso, mas não ordena por chave. Para ordenação — TreeMap.

Erro nº 5: adicionar null em ArrayDeque.
ArrayDeque não suporta elementos null — lançará NullPointerException.

Erro nº 6: comparar coleções sem considerar a ordem.
Ao comparar LinkedHashSet/LinkedHashMap com HashSet/HashMap comuns, leve em conta as diferenças na ordem dos elementos.

1
Pesquisa/teste
Trabalhando com coleções, nível 28, lição 4
Indisponível
Trabalhando com coleções
Trabalhando com coleções
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION