Go: Estructuras de datos

Golang: estructuras de datos fundamentales en Go. Aprende a usar arrays, slices, mapas y otras estructuras esenciales para desarrollar aplicaciones eficientes en Go.

Aprende Go GRATIS y certifícate

Las estructuras de datos son componentes esenciales en cualquier lenguaje de programación, y Golang no es la excepción. Comprender cómo manejar y aplicar estas estructuras en Go permite desarrollar aplicaciones más eficientes y escalables. A continuación, se exploran las estructuras de datos más utilizadas en Golang, con ejemplos prácticos y actualizados.

Arrays

En Golang, un array es una colección de elementos del mismo tipo con una longitud fija. Los arrays se declaran especificando el tipo y la cantidad de elementos que contienen.

var numeros [5]int

En este ejemplo, numeros es un array de enteros con una longitud de 5. También es posible inicializar un array con valores:

letras := [3]string{"a", "b", "c"}

Acceso y modificación de elementos

Los elementos de un array se acceden mediante índices, comenzando desde cero.

fmt.Println(letras[0]) // Imprime "a"

Para modificar un elemento:

letras[1] = "d"
fmt.Println(letras[1]) // Imprime "d"

Slices

Los slices son una vista flexible y dinámica de los arrays. A diferencia de los arrays, los slices pueden cambiar de tamaño. Se declaran sin especificar una longitud.

var numerosSlice []int

Creación y inicialización

Un slice puede crearse a partir de un array o usando la función integrada make.

numerosArray := [5]int{1, 2, 3, 4, 5}
numerosSlice := numerosArray[1:4] // Slice desde el índice 1 hasta el 3

// Usando make
letrasSlice := make([]string, 3)
letrasSlice[0] = "x"
letrasSlice[1] = "y"
letrasSlice[2] = "z"

Añadir elementos

Para añadir elementos a un slice, se utiliza la función append.

numerosSlice = append(numerosSlice, 6, 7)
fmt.Println(numerosSlice) // Imprime [2 3 4 6 7]

Capacidad y longitud

Los slices tienen una longitud y una capacidad. La longitud es el número de elementos en el slice, mientras que la capacidad es el número de elementos en el array subyacente a partir del punto inicial del slice.

fmt.Println(len(numerosSlice)) // Longitud del slice
fmt.Println(cap(numerosSlice)) // Capacidad del slice

Maps

Un map es una colección desordenada de pares clave-valor. Las claves deben ser de un tipo comparable, mientras que los valores pueden ser de cualquier tipo.

Creación y inicialización

Se utiliza la función make para crear un map.

edad := make(map[string]int)
edad["Juan"] = 30
edad["Ana"] = 25

// Declaración con valores iniciales
paises := map[string]string{
    "ES": "España",
    "MX": "México",
}

Acceso y modificación

Para acceder a un valor:

fmt.Println(edad["Juan"]) // Imprime 30

Para modificar o añadir un valor:

edad["Juan"] = 31

Verificar existencia de una clave

Al acceder a una clave, es posible verificar si existe.

valor, existe := edad["Pedro"]
if existe {
    fmt.Println("La edad de Pedro es", valor)
} else {
    fmt.Println("Pedro no está en el map")
}

Eliminación de elementos

Para eliminar una clave y su valor asociado, se utiliza delete.

delete(edad, "Ana")

Structs

Los structs son tipos de datos compuestos que agrupan campos. Son útiles para representar objetos o entidades con múltiples propiedades.

Definición y utilización

type Persona struct {
    Nombre string
    Edad   int
}

func main() {
    persona := Persona{
        Nombre: "Luis",
        Edad:   28,
    }
    fmt.Println(persona.Nombre) // Imprime "Luis"
}

Punteros a structs

Es posible utilizar punteros para referenciar structs.

func incrementarEdad(p *Persona) {
    p.Edad++
}

func main() {
    persona := Persona{"María", 22}
    incrementarEdad(&persona)
    fmt.Println(persona.Edad) // Imprime 23
}

Punteros

Los punteros almacenan la dirección de memoria de una variable. En Go, los punteros se utilizan para pasar referencias y modificar valores en funciones.

Declaración y uso

var numero int = 10
var punteroNumero *int = &numero

fmt.Println(*punteroNumero) // Imprime 10

*punteroNumero = 20
fmt.Println(numero) // Imprime 20

Interfaces

Las interfaces definen un conjunto de métodos que debe implementar un tipo. Permiten definir comportamientos comunes.

Definición y ejemplo

type Forma interface {
    Area() float64
}

type Circulo struct {
    Radio float64
}

func (c Circulo) Area() float64 {
    return 3.1416 * c.Radio * c.Radio
}

func calcularArea(f Forma) {
    fmt.Println("El área es:", f.Area())
}

func main() {
    circulo := Circulo{Radio: 5}
    calcularArea(circulo)
}

Canales

Los canales son una característica fundamental en Go para la comunicación entre goroutines. Permiten enviar y recibir valores.

Creación y uso

func enviarMensaje(c chan string) {
    c <- "Hola desde la goroutine"
}

func main() {
    mensaje := make(chan string)
    go enviarMensaje(mensaje)
    fmt.Println(<-mensaje) // Imprime "Hola desde la goroutine"
}

Conjuntos (Sets)

Aunque Go no tiene una estructura de datos de conjunto integrada, se pueden simular utilizando maps.

Implementación de un conjunto

conjunto := make(map[string]struct{})
conjunto["elemento1"] = struct{}{}
conjunto["elemento2"] = struct{}{}

if _, existe := conjunto["elemento1"]; existe {
    fmt.Println("El elemento1 está en el conjunto")
}

Listas enlazadas

La biblioteca estándar de Go proporciona implementaciones para listas.

Uso de list.List

import (
    "container/list"
)

func main() {
    lista := list.New()
    lista.PushBack(1)
    lista.PushBack(2)
    lista.PushBack(3)

    for elemento := lista.Front(); elemento != nil; elemento = elemento.Next() {
        fmt.Println(elemento.Value)
    }
}

Pilas y colas

Las pilas y colas pueden implementarse utilizando slices o la biblioteca estándar.

Implementación de una pila usando slices

type Pila []int

func (p *Pila) Push(valor int) {
    *p = append(*p, valor)
}

func (p *Pila) Pop() int {
    indice := len(*p) - 1
    valor := (*p)[indice]
    *p = (*p)[:indice]
    return valor
}

func main() {
    var pila Pila
    pila.Push(10)
    pila.Push(20)
    fmt.Println(pila.Pop()) // Imprime 20
}

Implementación de una cola usando slices

type Cola []int

func (c *Cola) Enqueue(valor int) {
    *c = append(*c, valor)
}

func (c *Cola) Dequeue() int {
    valor := (*c)[0]
    *c = (*c)[1:]
    return valor
}

func main() {
    var cola Cola
    cola.Enqueue(10)
    cola.Enqueue(20)
    fmt.Println(cola.Dequeue()) // Imprime 10
}

Árboles

Aunque Go no incluye árboles en su biblioteca estándar, es posible implementarlos.

Ejemplo de un árbol binario simple

type Nodo struct {
    Valor int
    Izq   *Nodo
    Der   *Nodo
}

func (n *Nodo) Insertar(valor int) {
    if valor <= n.Valor {
        if n.Izq == nil {
            n.Izq = &Nodo{Valor: valor}
        } else {
            n.Izq.Insertar(valor)
        }
    } else {
        if n.Der == nil {
            n.Der = &Nodo{Valor: valor}
        } else {
            n.Der.Insertar(valor)
        }
    }
}

func main() {
    raiz := &Nodo{Valor: 50}
    raiz.Insertar(30)
    raiz.Insertar(70)
    // ...continuar con la implementación
}
Empezar curso de Go

Lecciones de este módulo de Go

Lecciones de programación del módulo Estructuras de datos del curso de Go.

Ejercicios de programación en este módulo de Go

Evalúa tus conocimientos en Estructuras de datos con ejercicios de programación Estructuras de datos de tipo Test, Puzzle, Código y Proyecto con VSCode.