"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Mapas em Go

Mapas em Go

Publicado em 2024-08-24
Navegar:469

Maps in Go

Introdução

Go incorpora um tipo nativo que implementa tabelas hash chamadas map. É um tipo de dados composto por uma coleção de chaves exclusivas e uma coleção de valores para cada uma dessas chaves.
Pode ser comparado a um dicionário em outras línguas, por exemplo, que armazena pares de valores-chave. Esses valores são acessados ​​por meio de chaves, da mesma forma que arrays e slices como vimos no post anterior.
Os índices não são limitados a um número como em arrays ou fatias e os elementos não são ordenados, então se imprimirmos um mapa ele retornará uma ordem aleatória, se não fizermos nada para substituir sua impressão e forçar a ordem desejada.

Declaração e inicialização do mapa

Para declarar um mapa é feito com map[key]value, onde key será o tipo que queremos que nossa chave seja (deve ser de um tipo comparável https://go.dev/ref/spec#Comparison_operators ) e value será o tipo que queremos que o mapa seja armazenado em cada uma das chaves, seja qual for o tipo, de um int a uma struct, ou outro mapa, o que quisermos.

Assim como acontece com as fatias, os mapas são tipos referenciados, o que significa que o valor zero de um mapa será nulo.
Isso acontece porque embaixo dela existe uma tabela hash que armazena as chaves e os valores, e eles são simplesmente um envelope, uma abstração, deles.

Se declararmos como:

var m map[int]int

seu valor será nulo.

Se quisermos que tenha valor zero, podemos usar a declaração:

m := map[int]int{}

E podemos até inicializá-lo como as fatias, usando a função make.

m := make(map[string]string)

Fazer isso inicializará um mapa hash com o conjunto de memória apropriado para ele, retornando assim um mapa que aponta para essa estrutura de dados.

Adicionando e lendo valores de um mapa

A adição de valores a um mapa é feita usando chaves [] e chaves, assim como acontece com matrizes ou fatias. Neste exemplo criaremos um mapa com as chaves sendo strings e os valores sendo inteiros, para armazenar nomes e idades.

ages := make(map[string]int)

ages["John"] = 33
ages["Charly"] = 27
ages["Jenny"] = 45
ages["Lisa"] = 19

Se quisermos adicionar os valores a ele quando declararmos o mapa, podemos usar a declaração curta e fazer tudo na mesma etapa:

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}

Para ler os valores, basta indicar a chave do nosso mapa e ele retornará esse valor. Por exemplo, para descobrir a idade de Lisa, podemos fazer:

fmt.Println(ages["Lisa"]) // 19

Se tentarmos acessar uma chave que não existe, o valor obtido será o valor zero do tipo, neste caso seria "", por se tratar de uma string.

Para verificar se existe um elemento no mapa, podemos verificar se o tipo é o padrão, mas não é muito confiável, pois talvez exista, mas seu valor é uma string vazia ou 0 no caso de int , que corresponderia ao seu valor zero, então Go nos ajuda com o seguinte:

val, ok := ages["Randy"]

Se igualarmos o mapa a dois valores, o primeiro será o valor daquele elemento acessado através da chave, neste caso "Randy" que não existe, e o segundo será um booleano, que indicará se existe ou não.

Se não estivermos interessados ​​no valor e quisermos simplesmente verificar a existência de uma chave, podemos usar _ para ignorar o valor da seguinte forma:

_, ok := ages["Randy"]

Assim como acontece com arrays e fatias, podemos usar a função len para descobrir quantos elementos existem no mapa.

fmt.Println(len(ages)) // 4

Se quisermos modificar um valor, basta acessar esse valor usando uma chave e combiná-lo com outra, e ele será modificado.

Se declararmos um segundo mapa apontando para o primeiro, se modificarmos o valor do segundo, por ser um tipo referenciado, estaremos modificando o valor do primeiro, pois ambos compartilham a mesma tabela hash abaixo.

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
agesNew := ages
agesNew["Bryan"] = 77
fmt.Println(agesNew) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]
fmt.Println(ages) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]

Removendo valores de um mapa

Para excluir elementos de um mapa, Go nos fornece uma função de exclusão com a seguinte assinatura delete(m map[Type]Type1, key Type) que recebe um mapa e a chave a ser excluída.
No caso anterior, se quiséssemos eliminar "Lisa" faríamos isso:

delete(ages, "Lisa")

Percorrendo mapas

Se quisermos percorrer o conteúdo de um mapa, podemos fazê-lo usando um for com a variação de range que já vimos no post sobre arrays e slices.
Como então, o primeiro elemento será o índice, portanto a chave, e o segundo o valor.

for key, value := range ages {
    fmt.Printf("%s: %d\n", key, value)
}

// Output:
// Jenny: 45
// Lisa: 19
// John: 33
// Charly: 27

Assim como acontece com arrays e fatias, se estivermos interessados ​​apenas no valor, sem a chave, podemos omiti-lo usando _.

for _, value := range ages {
    fmt.Println(value)
}

// Output:
// 19
// 33
// 27
// 45

E se o que nos interessa é simplesmente a chave, podemos atribuir o intervalo a uma única variável para obtê-la:

for key := range ages {
    fmt.Println(key)
}

// Output:
// John
// Charly
// Jenny
// Lisa

Classificar um mapa

Como mencionei na introdução, em um mapa as informações não são ordenadas, portanto, ao percorrê-lo, não podemos especificar a ordem que segue, nem Go pode garantir que a ordem entre as execuções seja a mesma.
Como vimos com arrays e fatias, na biblioteca padrão existe um pacote sort que nos ajuda a classificar os elementos: https://pkg.go.dev/sort

Seguindo nosso exemplo com idades e usando sort, podemos ordenar as chaves do mapa antes de percorrê-lo e assim garantir que ele será acessado na ordem.

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
keys := make([]string, 0, len(ages))
for k := range ages {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Println(k, ages[k])
}

// Output:
// Charly 27
// Jenny 45
// John 33
// Lisa 19

Declaramos nosso mapa de idades com a declaração curta como vimos antes.
Criamos uma string slices para armazenar as chaves e usamos o método make com comprimento 0, já que não temos nenhuma chave no momento, mas reservamos a capacidade que ela terá usando o método len para o comprimento do nosso mapa.
Percorremos o mapa de idades para manter suas chaves e adicioná-las à fatia criada.
Classificamos as chaves em ordem alfabética com a função sort.Strings.
Percorremos a fatia de chaves, já ordenadas, e acessamos o mapa com a chave em questão.
Desta forma acessaremos o mapa de forma ordenada e poderemos fazer a lógica que nosso programa necessita.

Problemas com simultaneidade

Algo a ter em mente com relação aos mapas é que eles não são seguros para uso simultâneo. Se forem leituras simultâneas, acessando um valor ou por meio de um for com um intervalo, não há problema com vários goroutines acessando-o ao mesmo tempo.
O caso problemático é quando você deseja atualizar o valor de um mapa simultaneamente, seja adicionando ou removendo elementos dele, e ao mesmo tempo está lendo-o de outro lado, por exemplo.
Para resolver esta situação existem várias soluções possíveis, que não irei entrar em muitos detalhes, apenas mencionarei e deixarei a sua escolha aprofundá-las.

Se usarmos o pacote de sincronização: https://pkg.go.dev/sync da biblioteca padrão, podemos controlar a sincronia entre as diferentes goroutines.
Um uso possível é o tipo RWMutex que nos permite bloquear e desbloquear leituras e gravações em um tipo. Portanto, se tivermos um tipo que contém um sync.RWMutex e um mapa, podemos controlar quando ele pode ser acessado.
Outro tipo interessante para investigar dentro do mesmo pacote de sincronização é o Map, que já nos oferece uma série de funções que nos ajudarão a trabalhar com o nosso mapa, com as quais no final não poderemos trabalhar nativamente, como na solução anterior.
Dependendo do caso de uso que estamos implementando, um ou outro nos será mais útil, e não há ninguém melhor que o outro, sempre dependerá do que precisamos.

Espero que tudo o que tentei explicar neste post tenha ficado claro e, por favor, se houver alguma parte que não tenha sido completamente clara ou se houver partes que não abordei e que você gostaria que eu fizesse, deixe me deixe um comentário aqui mesmo ou através das minhas redes sociais que você tem no meu perfil e terei prazer em responder.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/charly3pins/maps-in-go-5a3j?1 Se houver alguma violação, entre em contato com [email protected] para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3