Go no tiene Set: por qué y cómo vivir sin un conjunto incorporado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

«¿Dónde está mi Set?» — el grito del alma del desarrollador Go

\n\n

Si vienes a Go desde Python, JavaScript o Java, tarde o temprano te preguntarás: «¿Dónde diablos está mi querido Set?» — y es normal. En Stack Overflow, el tema «Golang why don't we have a set datastructure» ha acumulado miles de visitas y decenas de acalorados debates. Los desarrolladores están acostumbrados a un conjunto conveniente con elementos únicos, pero Go solo ofrece arrays, slices y maps. En este artículo analizaremos la filosofía de los creadores de Go, aprenderemos a emular Set e incluso escribiremos nuestra propia implementación minimalista.

\n\n

¿Por qué los creadores de Go no agregaron Set a la biblioteca estándar?

\n\n

La respuesta está en el minimalismo y pragmatismo del lenguaje. El equipo de Go (Rob Pike, Ken Thompson, Robert Griesemer) se adhirió al principio: «Si algo se puede hacer con una sola construcción del lenguaje, no multipliques las entidades». En Go, map no es solo un diccionario, sino un contenedor universal. Ya soporta la unicidad de las claves, que es la propiedad principal de un conjunto.

\n\n

Filosofía «menos magia, más claridad»

\n\n

A diferencia de Python, donde set es un tipo separado con sus propios métodos (.add(), .union()), en Go decidieron no complicar el lenguaje. ¿Para qué escribir una estructura separada si map[T]struct{} hace lo mismo? Esto ahorra espacio en la biblioteca estándar y reduce la barrera de entrada para los principiantes. Lo principal es aprender a usar correctamente map como set.

\n\n

Cómo emular Set en Go: tres enfoques prácticos

\n\n

Pasemos a la práctica. Así es como se puede implementar un conjunto usando solo las herramientas estándar de Go.

\n\n

1. El más popular: map[type]struct{}

\n\n

Este es el enfoque estándar en la comunidad Go. ¿Por qué struct{}? Porque la estructura vacía no ocupa memoria (0 bytes) — ideal como marca de «existencia» del elemento.

\n\n
package main\n\nimport "fmt"\n\nfunc main() {\n    // Creamos un conjunto de números enteros\n    set := make(map[int]struct{})\n\n    // Agregamos elementos\n    set[1] = struct{}{}\n    set[2] = struct{}{}\n    set[3] = struct{}{}\n    set[1] = struct{}{} // El duplicado se ignora\n\n    // Verificación de existencia\n    if _, exists := set[2]; exists {\n        fmt.Println("2 está en el conjunto")\n    }\n\n    // Eliminación\n    delete(set, 3)\n\n    // Iteración\n    for key := range set {\n        fmt.Println(key)\n    }\n}
\n\n

Ventajas: asignación de memoria cero para los valores, simplicidad. Desventajas: no hay métodos .Add() o .Contains() — todo se hace a través de la sintaxis de map.

\n\n

2. Tipo propio con métodos

\n\n

Si necesita una API limpia, envuelva el map en una estructura:

\n\n
package main\n\nimport "fmt"\n\n// Set — conjunto genérico (Go 1.18+)\ntype Set[T comparable] struct {\n    items map[T]struct{}\n}\n\n// NewSet crea un nuevo Set\nfunc NewSet[T comparable]() *Set[T] {\n    return &Set[T]{items: make(map[T]struct{})}\n}\n\n// Add agrega un elemento\nfunc (s *Set[T]) Add(item T) {\n    s.items[item] = struct{}{}\n}\n\n// Contains verifica la presencia\nfunc (s *Set[T]) Contains(item T) bool {\n    _, ok := s.items[item]\n    return ok\n}\n\n// Size devuelve el tamaño\nfunc (s *Set[T]) Size() int {\n    return len(s.items)\n}\n\nfunc main() {\n    intSet := NewSet[int]()\n    intSet.Add(10)\n    intSet.Add(20)\n    fmt.Println(intSet.Contains(10)) // true\n    fmt.Println(intSet.Size())       // 2\n}
\n\n

Esto ya se parece a una set datastructure en Go completa. Los genéricos (generics) aparecieron en la versión 1.18, por lo que el código funciona en proyectos modernos.

\n\n

3. Uso de bool en lugar de struct{}

\n\n

Algunos usan map[T]bool, pero esto gasta 1 byte por valor. En la mayoría de los casos la diferencia es imperceptible, pero si está escribiendo un servicio de alta carga, es mejor ceñirse a struct{}.

\n\

Blogs

Book Recommendations