Golang básico - Arrays, Slices e Mapas

Bruno Gomes - May 21 '22 - - Dev Community

Esse post faz parte de uma série onde pretendo compartilhar o básico essencial para desenvolver com GoLang.

No post anterior entendemos um pouco melhor de tipo de dados e ao fim eu mencionei alguns tipos de referência, nesse post vou dar um pouco mais detalhes, principalmente de Slices e Mapas, acredito que será bem importante para avançarmos para os próximos posts.

Estrutura de dados

Esse é um tópico bastante extenso, e um dos mais importantes dentro da ciência de computação, pode ser um pouco difícil para quem está iniciando, mas garanto que fica prazeroso de estudar e ver o quão genial são todas as possibilidades de estruturas.

Arrays

Um array é uma coleção de valores, nós vimos as variáveis, cada uma com seu tipo. O array vai te possibilitar ter vários valores de um mesmo tipo.

package main

import (
    "fmt"
)

func main() {
    // pode ser declarado com var seguido do [tamanho]tipo
    var animes [3]string
    animes[0] = "Gantz"
    animes[1] = "Berserk"
    animes[2] = "Attack on Titan"
    fmt.Println(animes)

    // também pode utilizar o operador ':=' e passar os valores entre as chaves
    personagens := [3]string{"Kurono", "Guts", "Eren"}
    fmt.Println(personagens)

    // ou substituindo o tamanho por '...'
    personagens = [...]string{"Kei Kishimoto", "Casca", "Mikasa"}
    fmt.Println(personagens)
}
Enter fullscreen mode Exit fullscreen mode

Uma coisa que fica evidente, arrays tem o tamanho definido em sua criação e eles são de tamanho fixo!

Slices

São muito similares aos Arrays, porém são dinâmicos e muito mais fáceis de trabalhar, a forma mais simples de criar um Slice é utilizando a função make:

// make recebe o tipo []string e a capacidade inicial do slice
animes := make([]string, 3)
Enter fullscreen mode Exit fullscreen mode

Perceba que a tipagem é muito similar a do array, e inicialmente o uso também é parecido. Bom, uma coisa importante de saber é que os Slices tem 3 propriedades:

  • ponteiro para um array (novamente ponteiros, em breve falaremos disso!)
  • len que é abreviação de length, o tamanho atual do array.
  • cap abreviação de capacity, quantos valores ele suporta armazenar.
package main

import (
    "fmt"
)

func main() {
    // cria um slice com tamanho 3
    animes := make([]string, 3)
    animes[0] = "Gantz"
    animes[1] = "Berserk"
    animes[2] = "Attack on Titan"
    fmt.Println(animes)
    fmt.Println(len(animes), cap(animes))

    // a função append redimensiona a capacidade do slice
    animes = append(animes, "Mob Psycho")
    fmt.Println(animes)
    fmt.Println(len(animes), cap(animes))
}

Enter fullscreen mode Exit fullscreen mode

Tanto slices quanto arrays podem ser acessados pelo índice do valor:

personagens := [...]string{"Kei Kishimoto", "Casca", "Mikasa"}
fmt.Println(personagens[2])
Enter fullscreen mode Exit fullscreen mode

E existe uma sintaxe muito interessante chamada slice-expression, que é uma maneira de extrair sub arrays/slices utilizando array[inicio:fim], por exemplo:

package main

import (
    "fmt"
)

func main() {
    personagens1 := [3]string{"Kurono", "Guts", "Eren"}
    fmt.Println(personagens1)
    fmt.Println(personagens1[0:2])

    personagens2 := [3]string{"Kei Kishimoto", "Casca", "Mikasa"}
    fmt.Println(personagens2)
    fmt.Println(personagens2[2:3])
}
Enter fullscreen mode Exit fullscreen mode

Quando usar Array e quando usar Slice ?

Na minha opinião o mais comum é utilizar Slices, por ter essa característica mais dinâmica. Isso simplifica muito o código, te evita ter que ficar recriando arrays e redimensionando.

É uma boa usar array quando você sabe exatamente o número de valores que precisa armazenar, ou se você quer fazer uma solução super otimizada, pelo fato de arrays não fazerem uso direto de um ponteiro 👀.

Vou deixar algumas referências, que explicam a diferença entre os dois bem melhor do que eu:

Map

Essa estrutura de dados vai fazer parte da sua vida pra sempre, brincadeiras a parte, ela resolve uma série de problemas e normalmente te ajuda a deixar o seu código mais otimizado.
As principais características de um mapa é que ele é uma coleção de pares, ou melhor, de chaves e valores. E ele não é ordenado, pois trabalha com o conceito de hash, ou espalhamento.
Em outras linguagens você vai encontrar Map com outros nomes, por exemplo: dict, hash, Hashtable e HashMap

package main

import (
    "fmt"
)

func main() {
    // são iniciados com make, é opcional informar a capacidade inicial
    // e assim é definido map[tipo da chave]tipo do valor
    dicionario := make(map[string]string)
    dicionario["hi"] = "oi"
    dicionario["bye"] = "tchau"
    dicionario["hey"] = "opa"
    fmt.Println(dicionario)

    // também podem ser iniciados com valores literais
    personagemAnime := map[string]string{
        "Eren":   "Attack on Titan",
        "Guts":   "Berserk",
        "Kurono": "Gantz",
    }
    fmt.Println(personagemAnime)
}

Enter fullscreen mode Exit fullscreen mode

Modificando Map

Quando você estiver resolvendo um problema com um mapa, seja um de-para, um agrupamento de dados, sumarizando totais, etc. Você vai precisar de forma incremental modificar o mapa, e para isso você precisa conhecer algumas poucas operações:

// recuperar um valor associado a uma chave
anime := personagemAnime["Eren"]
fmt.Println(anime)
Enter fullscreen mode Exit fullscreen mode
// recuperar um valor associado a uma chave, e conferir se ele existe
anime, ok := personagemAnime["Naruto"]
// a variavel ok vai ser um bool sinalizando se a chave existe ou não.
fmt.Println(anime, ok)
Enter fullscreen mode Exit fullscreen mode
// deleta a chave e o valor associado do mapa
delete(personagemAnime, "Kurono")
Enter fullscreen mode Exit fullscreen mode
// para atualizar ou inserir 
personagemAnime["Kurono"] = "Gantz"
Enter fullscreen mode Exit fullscreen mode

Desde a criação até essas operações a forma de manusear um mapa é muito parecida com slice/array. Esses pequenos detalhes fazem do Go uma linguagem tão simples.

E agora?

Voltaremos a ver Slices e Mapas quando falarmos de controle de fluxo e iterações!

Nesse momento eu vou limitar nas estruturas simples do Go, até nesse sentido a linguagem foi bastante simplificada tendo um número bem reduzido de estrutura de dados se comparado a outras linguagens. Por um lado isso é bom, pois facilita muito para quem é iniciante, o estudo é mais curto. Mas por favor, não pare nas estruturas básicas do Go 🙏.

Vou deixar algumas referências que podem ajudar a seguir com os estudos:

Junmin Lee - Tem uma série de estrutura de dados implementadas em Go

Programação Dinâmica - Conta com série de estrutura de dados em português.
Inclusive esse canal tem muito material bom com uma didática excelente, vale muito a pena se inscrever!

Resumo

  • arrays são coleções de um mesmo tipo de tamanho fixo.
  • slices são coleções de um mesmo tipo com tamanho dinâmico.
  • mapas são coleções não ordenadas de chaves e valores.
  • slices e mapas são inicializados com a função make()
. . . . . . . .