Historia de la pregunta:
Go por defecto no tiene una estructura Set, pero a menudo surge la necesidad de trabajar con elementos únicos. La estructura óptima fue map[string]struct{}, donde la clave es el elemento y la estructura vacía sirve como "marca de presencia". Este es un patrón comúnmente utilizado para pruebas de pertenencia rápidas.
Problema:
La falta de un Set incorporado conduce a que los principiantes crean que es difícil implementar correctamente colecciones únicas. También es necesario entender por qué struct{} es más eficiente que bool o int como valor.
Solución:
Para implementar un Set en Go se utiliza map[string]struct{}. La estructura vacía struct{} no requiere memoria (zero-sized), y el map proporciona acceso rápido. Ejemplo:
set := make(map[string]struct{}) set["foo"] = struct{}{} if _, ok := set["foo"]; ok { fmt.Println("Presente") } delete(set, "foo")
Características clave:
¿Por qué no se puede usar slice/arreglo como valor?
slice/arreglo para set no da tiempo de búsqueda constante — tendrás que iterar sobre todos los valores, lo cual es lento.
¿Cuál es la diferencia entre map[string]struct{} y map[string]bool?
map[string]bool ocupa más memoria: para cada clave se almacena un bool, mientras que struct{} es un tipo vacío que no aloca nada.
set := map[string]bool{"foo": true}
¿Se puede usar int en lugar de struct{}?
Se puede, pero int siempre ocupa memoria. struct{} es más versátil: si solo se necesita el papel de "marca" (presencia), es mejor.
set := map[string]int{"foo": 1} // pero almacena (clave -> número)
Debido a la falta de conocimiento, se asignó map[string]bool para un conjunto de direcciones IP únicas. Como resultado, con millones de direcciones, el consumo de memoria se duplicó en comparación con struct{}.
Pros:
Contras:
En un proyecto se utilizó map[string]struct{} para almacenar correos electrónicos únicos. La carga se redujo, funcionó más rápido, casi no se gastó memoria en los valores.
Pros:
Contras: