Срезы и карты в Go: мощные структуры данных

В этом уроке мы изучим две фундаментальные структуры данных в Go — срезы (slices) и карты (maps). Эти инструменты являются основой для работы с коллекциями данных и широко используются в реальных проектах.

Почему срезы и карты важны?

Срезы и карты — это ключевые структуры данных в Go, которые:

  • Позволяют эффективно работать с коллекциями
  • Предоставляют гибкость в управлении данными
  • Оптимизированы для производительности
  • Используются в большинстве Go-программ

💡 Интересный факт: В Go нет встроенных динамических массивов, но срезы предоставляют всю необходимую функциональность и даже больше.

Срезы (Slices)

1. Что такое срез?

Срез — это динамическая обёртка над массивом, которая предоставляет:

  • Гибкий размер
  • Эффективное управление памятью
  • Удобные операции с данными
// Создание среза разными способами
slice1 := []int{1, 2, 3}           // Литерал среза
slice2 := make([]int, 3)           // С помощью make
slice3 := make([]int, 3, 5)        // С указанием длины и ёмкости

2. Основные операции со срезами

// Добавление элементов
numbers := []int{1, 2, 3}
numbers = append(numbers, 4, 5)    // [1, 2, 3, 4, 5]

// Удаление элемента
numbers = append(numbers[:2], numbers[3:]...) // [1, 2, 4, 5]

// Копирование среза
copySlice := make([]int, len(numbers))
copy(copySlice, numbers)

3. Длина и ёмкость

slice := make([]int, 3, 5)
fmt.Println(len(slice))  // 3 (текущая длина)
fmt.Println(cap(slice))  // 5 (максимальная ёмкость)

// При превышении ёмкости Go автоматически увеличивает срез
slice = append(slice, 1, 2, 3)
fmt.Println(cap(slice))  // 10 (удвоенная ёмкость)

Карты (Maps)

1. Что такое карта?

Карта — это коллекция пар "ключ-значение", которая:

  • Обеспечивает быстрый доступ к данным
  • Поддерживает уникальные ключи
  • Позволяет эффективно искать значения
// Создание карты
ages := map[string]int{
    "Иван": 25,
    "Пётр": 30,
}

// Добавление элемента
ages["Анна"] = 28

// Проверка существования ключа
if age, exists := ages["Иван"]; exists {
    fmt.Printf("Возраст: %d\n", age)
}

2. Операции с картами

// Удаление элемента
delete(ages, "Пётр")

// Итерация по карте
for name, age := range ages {
    fmt.Printf("%s: %d лет\n", name, age)
}

// Очистка карты
for key := range ages {
    delete(ages, key)
}

3. Особенности карт

// Нулевая карта
var emptyMap map[string]int
fmt.Println(emptyMap == nil) // true

// Создание карты с начальной ёмкостью
scores := make(map[string]int, 100)

// Использование struct{} для множеств
unique := make(map[string]struct{})
unique["value"] = struct{}{}

Практические примеры

1. Фильтрация данных

func filterEven(numbers []int) []int {
    result := []int{}
    for _, num := range numbers {
        if num%2 == 0 {
            result = append(result, num)
        }
    }
    return result
}

2. Подсчёт частоты слов

func countWords(text string) map[string]int {
    words := strings.Fields(text)
    frequency := make(map[string]int)
    
    for _, word := range words {
        frequency[word]++
    }
    
    return frequency
}

3. Кэширование результатов

type Cache struct {
    data map[string]interface{}
    mu   sync.RWMutex
}

func (c *Cache) Get(key string) (interface{}, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()
    value, exists := c.data[key]
    return value, exists
}

Практические задания

Задание 1: Анализатор текста

Создайте программу, которая:

  1. Принимает текст от пользователя
  2. Подсчитывает частоту каждого слова
  3. Выводит топ-5 самых часто встречающихся слов
  4. Сохраняет результаты в карту

Задание 2: Система управления задачами

Напишите программу, которая:

  1. Позволяет добавлять задачи
  2. Отмечать задачи как выполненные
  3. Удалять задачи
  4. Показывать список всех задач
  5. Использует срезы для хранения задач

Задание 3: Калькулятор статистики

Создайте программу, которая:

  1. Принимает список чисел
  2. Вычисляет:
    • Среднее значение
    • Медиану
    • Моду
    • Стандартное отклонение
  3. Использует срезы для хранения и обработки данных

Что дальше?

В следующем уроке мы:

  • Изучим указатели в Go
  • Познакомимся с интерфейсами
  • Узнаем о методах и получателях
  • Начнём писать более сложные программы

🎯 Цель урока: К концу этого урока вы должны уметь:

  • Эффективно работать со срезами и картами
  • Применять их в реальных задачах
  • Понимать особенности их реализации
  • Оптимизировать работу с коллекциями данных