Removendo chave de map em Go: o que acontece internamente?

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Removendo chave de map: como funciona internamente no Go

\n\n

Você já se perguntou o que realmente acontece quando você escreve delete(myMap, "key") em Go? A chave simplesmente desaparece da memória? Ou fica um "buraco"? E se você tentar remover uma chave de um nil map? Se você já se fez essas perguntas, não está sozinho. O tópico remover chave de map já gerou milhares de visualizações no Stack Overflow, e hoje vamos explorar os bastidores dessa operação aparentemente simples.

\n\n

Neste artigo otimizado para SEO, vamos analisar como a hash table funciona em Go (estrutura hmap), como a remoção de chave é realizada, para onde vai a memória e por que você não deve ter medo de usar delete em loops. Vamos nessa!

\n\n

Uma visão geral: como o map funciona no Go

\n\n

Antes de falarmos sobre remoção, vamos lembrar como o map em Go armazena dados. Internamente, é uma hash table, representada pela estrutura hmap. Ela consiste em um array de "baldes" (buckets), cada um capaz de armazenar até 8 pares chave-valor. Quando você adiciona uma chave, o Go calcula o hash, encontra o balde e insere o par. Se o balde transborda, uma cadeia de baldes de estouro (overflow bucket) é criada.

\n\n

Essa é uma implementação clássica, mas com alguns truques que discutiremos a seguir.

\n\n

Estrutura hmap (simplificada)

\n\n
type hmap struct {\n    count     int     // número de elementos\n    flags     uint8\n    B         uint8   // logaritmo da quantidade de baldes\n    buckets   unsafe.Pointer // ponteiro para o array de baldes\n    oldbuckets unsafe.Pointer // durante o crescimento do map\n    nevacuate uintptr\n    // ...\n}
\n\n

O ponto chave: buckets é um array contínuo. A remoção não compacta esse array, apenas marca a célula como "vazia".

\n\n

Removendo chave de map: como funciona no nível do código

\n\n

A chamada delete(m, k) em Go é uma função embutida que é compilada para uma chamada de runtime. Internamente, acontece mais ou menos o seguinte:

\n\n
    \n
  1. Cálculo do hash da chave — o Go usa uma função de hash específica para o tipo da chave.
  2. \n
  3. Localização do balde — os bits menos significativos do hash determinam o número do balde.
  4. \n
  5. Busca linear dentro do balde (e das cadeias de estouro) — encontra a célula com a chave desejada.
  6. \n
  7. Limpeza da memória — o Go zera a memória da chave e do valor para que o garbage collector possa coletá-los.
  8. \n
  9. Marcação da célula como "vazia" — usando flags especiais como emptyRest ou emptyOne.
  10. \n
  11. Decremento do contador — m.count--.
  12. \n
\n\n

Tudo isso ocorre em O(1) na média, mas considerando colisões, pode chegar a O(n) no pior caso.

\n\n

Exemplo de código: delete em ação

\n\n
package main\n\nimport "fmt"\n\nfunc main() {\n    m := map[string]int{\n        "apple":  5,\n        "banana": 3,\n        "cherry": 7,\n    }\n\n    fmt.Println("Antes da remoção:", m)\n    delete(m, "banana")\n    fmt.Println("Após a remoção:", m)\n\n    // Tentando remover uma chave inexistente\n    delete(m, "grape") // nada acontece, sem erro\n    fmt.Println("Após remover chave inexistente:", m)\n}
\n\n

Observe: remover chave de map para uma chave inexistente é uma operação segura. Ela simplesmente não faz nada. Esse é um dos equívocos comuns entre iniciantes.

\n\n

O que acontece com a memória? Mitos e realidade

\n\n

Muitos desenvolvedores temem que após um delete o map fique com "buracos", causando vazamentos de memória. Vamos esclarecer isso.

\n\n

Memória para chaves e valores

\n\n

Quando você remove uma chave, o Go zera a memória daquele elemento. Se a chave ou o valor forem ponteiros ou contiverem ponteiros (strings, slices, maps), o garbage collector poderá coletá-los. A célula em si no balde permanece, mas é marcada como vazia.

\n\n

Isso significa que o map não diminui de tamanho após a remoção. O array de baldes continua do mesmo tamanho. No entanto, a memória ocupada pelos dados antigos é liberada para o GC.

Blogs

Book Recommendations