Множества (Set) в Go 1.21: подробное руководство
Множества (Set) в Go 1.21
Что такое множество и зачем оно нужно?
Представь, что у тебя есть коробка, в которую можно положить только уникальные предметы. Если ты попробуешь положить туда два одинаковых яблока, то в коробке останется только одно. Это и есть множество (Set) — коллекция, в которой каждый элемент встречается только один раз.
Зачем это нужно? Например:
- Проверить, есть ли товар в магазине
- Убрать повторяющиеся ингредиенты из рецепта
- Объединить списки гостей без дубликатов
В Go 1.21 нет встроенного типа "множество", но мы можем легко его создать с помощью map!
Как создать множество в Go?
Мы будем использовать map[string]struct{}. Почему struct{}? Потому что это пустая структура, которая не занимает памяти — идеально для хранения только ключей!
// Создаём пустое множество
contacts := make(map[string]struct{})
// Добавляем элементы
contacts["Анна"] = struct{}{}
contacts["Борис"] = struct{}{}
contacts["Анна"] = struct{}{} // Повторно — ничего не изменится
// Проверяем, есть ли элемент
if _, exists := contacts["Анна"]; exists {
fmt.Println("Анна есть в контактах!")
}
// Удаляем элемент
delete(contacts, "Борис")
// Количество элементов
fmt.Println(len(contacts)) // Выведет: 1
Основные операции с множествами
1. Создание и добавление элементов
package main
import "fmt"
func main() {
// Создаём множество для ингредиентов пиццы
ingredients := make(map[string]struct{})
// Добавляем ингредиенты
ingredients["сыр"] = struct{}{}
ingredients["томаты"] = struct{}{}
ingredients["сыр"] = struct{}{} // Повторно не добавится
fmt.Println("Ингредиенты:", ingredients) // map[сыр:{} томаты:{}]
fmt.Println("Количество:", len(ingredients)) // 2
}
2. Проверка наличия элемента
package main
import "fmt"
func main() {
products := map[string]struct{}{
"молоко": {},
"хлеб": {},
"яйца": {},
}
var item string
fmt.Print("Введите товар для проверки: ")
fmt.Scan(&item)
if _, exists := products[item]; exists {
fmt.Println("Товар есть в наличии!")
} else {
fmt.Println("Товара нет в наличии.")
}
}
3. Удаление элементов
package main
import "fmt"
func main() {
shoppingList := map[string]struct{}{
"молоко": {},
"хлеб": {},
"яйца": {},
"сыр": {},
}
fmt.Println("Список покупок:", shoppingList)
// Удаляем ненужный товар
delete(shoppingList, "молоко")
fmt.Println("После удаления:", shoppingList)
}
4. Объединение множеств
package main
import "fmt"
func main() {
// Список покупок хозяина
hostList := map[string]struct{}{
"сок": {},
"чипсы": {},
"пицца": {},
}
// Что предложили гости
guestList := map[string]struct{}{
"сок": {},
"торт": {},
"лимонад": {},
}
// Объединяем — добавляем всё из guestList в hostList
for item := range guestList {
hostList[item] = struct{}{}
}
fmt.Println("Финальный список покупок:")
for item := range hostList {
fmt.Println("-", item)
}
}
Разбор типичных задач
Задача: Ингредиенты пиццы (уникальные)
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Print("Введите ингредиенты через запятую: ")
var input string
fmt.Scan(&input)
// Разбиваем строку на части
items := strings.Split(input, ",")
// Создаём множество для уникальных ингредиентов
uniqueIngredients := make(map[string]struct{})
for _, item := range items {
// Убираем лишние пробелы и добавляем
cleanItem := strings.TrimSpace(item)
if cleanItem != "" {
uniqueIngredients[cleanItem] = struct{}{}
}
}
fmt.Println("Уникальные ингредиенты:")
for ingredient := range uniqueIngredients {
fmt.Println("-", ingredient)
}
}
Ввод: сыр, томаты, сыр, грибы, томаты
Вывод: сыр, томаты, грибы
Задача: Поиск отсутствующих товаров
package main
import "fmt"
func main() {
// Товары, которые всегда должны быть
required := map[string]struct{}{
"молоко": {},
"хлеб": {},
"яйца": {},
"сыр": {},
}
// Спрашиваем, что есть
fmt.Println("Какие товары есть? (вводите по одному, 'стоп' для завершения)")
available := make(map[string]struct{})
for {
var item string
fmt.Print("Товар: ")
fmt.Scan(&item)
if item == "стоп" {
break
}
available[item] = struct{}{}
}
// Ищем, чего не хватает
fmt.Println("Нужно докупить:")
for item := range required {
if _, exists := available[item]; !exists {
fmt.Println("-", item)
}
}
}
Частые ошибки новичков
- Забывают проверить существование элемента — всегда используй
if _, exists := set[key]; exists { ... } - Пытаются получить значение по ключу — в множестве нет значений, только ключи. Используй проверку существования.
- Путают map[string]bool и map[string]struct{} —
struct{}{}занимает 0 байт, аbool— 1 байт. Для множеств лучшеstruct{}{}. - Не очищают пробелы — при вводе через запятую остаются пробелы. Всегда используй
strings.TrimSpace().
Итоговый пример: Записная книжка контактов
package main
import "fmt"
func main() {
// Множество для хранения уникальных имён
contacts := make(map[string]struct{})
fmt.Println("=== Записная книжка контактов ===")
fmt.Println("Команды: добавить [имя], удалить [имя], показать, выход")
for {
fmt.Print("\n> ")
var command, name string
fmt.Scan(&command)
switch command {
case "добавить":
fmt.Scan(&name)
contacts[name] = struct{}{}
fmt.Println("Контакт", name, "добавлен!")
case "удалить":
fmt.Scan(&name)
delete(contacts, name)
fmt.Println("Контакт", name, "удалён!")
case "показать":
if len(contacts) == 0 {
fmt.Println("Контактов пока нет.")
} else {
fmt.Println("Ваши контакты:")
for contact := range contacts {
fmt.Println("-", contact)
}
}
case "выход":
fmt.Println("До свидания!")
return
default:
fmt.Println("Неизвестная команда. Попробуйте: добавить, удалить, показать, выход")
}
}
}
Пример работы:
> добавить Анна
Контакт Анна добавлен!
> добавить Борис
Контакт Борис добавлен!
> добавить Анна
Контакт Анна добавлен! // Повторно не добавится
> показать
Ваши контакты:
- Анна
- Борис
> удалить Анна
Контакт Анна удалён!
> показать
Ваши контакты:
- Борис
> выход
До свидания!
Теперь ты знаешь, как использовать множества в Go! Они помогают хранить только уникальные значения и быстро проверять, есть ли элемент в коллекции. Используй map[string]struct{}{} для создания множества и не забывай проверять существование ключей через if _, exists := ....