Historia pytania:
Go domyślnie nie ma struktury Set, ale często pojawia się potrzeba pracy z unikalnymi elementami. Optymalną strukturą jest map[string]struct{}, gdzie klucz to element, a pusta struktura służy jako "znacznik obecności". To powszechny wzorzec do szybkiego testowania obecności.
Problem:
Brak wbudowanego Set sprawia, że nowicjuszom wydaje się trudne poprawne zaimplementowanie unikalnych kolekcji. Należy również zrozumieć, dlaczego struct{} jest bardziej efektywne niż bool lub int jako wartość.
Rozwiązanie:
Aby zaimplementować Set w Go, używają map[string]struct{}. Pusta struktura struct{} nie wymaga pamięci (zero-sized), a map zapewnia szybki dostęp. Przykład:
set := make(map[string]struct{}) set["foo"] = struct{}{} if _, ok := set["foo"]; ok { fmt.Println("Obecny") } delete(set, "foo")
Kluczowe cechy:
Dlaczego nie można używać slice/tablicy jako wartości?
slice/tablica dla zestawu nie daje stałego czasu wyszukiwania elementu — trzeba będzie przeszukiwać wszystkie wartości, co jest wolne.
Czym różni się map[string]struct{} od map[string]bool?
map[string]bool zajmuje więcej pamięci: dla każdego klucza przechowuje bool, a struct{} — pusty typ, który nic nie alokuje.
set := map[string]bool{"foo": true}
Czy można używać int zamiast struct{}?
Można, ale int zawsze zajmuje pamięć. struct{} jest uniwersalny: jeśli potrzebna jest tylko rola "znacznika" (obecności), jest lepszy.
set := map[string]int{"foo": 1} // ale przechowuje (klucz -> liczba)
Z powodu braku wiedzy użyto map[string]bool dla zestawu unikalnych adresów IP. W rezultacie, przy milionach adresów zużycie pamięci wzrosło dwukrotnie w porównaniu do struct{}.
Zalety:
Wady:
W projekcie do przechowywania unikalnych emaili zastosowano map[string]struct{}. Obciążenie zmniejszyło się, działało szybciej, pamięć prawie nie była zużywana na wartości.
Zalety:
Wady: