← Назад к вопросам

В чём разница между длиной и ёмкостью массива?

1.6 Junior🔥 261 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между длиной и ёмкостью в Go

В языке Go понятия длины (length) и ёмкости (capacity) являются фундаментальными, но часто путаемыми, особенно при работе со срезами (slices). Важно понимать, что для массивов (arrays) эти понятия ведут себя иначе, чем для срезов.

Для массивов

Массив в Go — это тип данных с фиксированным размером. Длина массива определяется при его объявлении и не может быть изменена в runtime.

// Массив из 5 целых чисел
var arr [5]int // Длина = 5, ёмкость = 5

Для массива:

  • Длина (length) — это количество элементов, которые содержатся в массиве. Для массива она постоянна.
  • Ёмкость (capacity) — для массива равна длине. Понятие ёмкости для массива практически не используется, так как массив не может "расти" или иметь "запас" памяти.

Функции len() и cap() для массива вернут одинаковое значение:

arr := [3]string{"a", "b", "c"}
fmt.Println(len(arr)) // 3
fmt.Println(cap(arr)) // 3 (ёмкость равна длине)

Для срезов (где разница критически важна)

Срез (slice) — это динамическая обёртка над массивом. Именно при работе со срезами различие между длиной и ёмкостью становится ключевым.

  • Длина (length) — текущее количество элементов в срезе, к которым можно обратиться по индексу (slice[0], slice[1] и т.д.). Это то, что видит пользователь среза.
  • Ёмкость (capacity) — общее количество элементов в базовом массиве (underlying array), начиная с первого элемента среза. Это "запас памяти", выделенный для будущего роста среза без переаллокации (копирования в новый массив большего размера).
// Создаём срез с начальной длиной 3 и ёмкостью 5
mySlice := make([]int, influence 3, 5) // Длина = 3, Ёмкость = 5

mySlice[0] = 1 // OK
mySlice[1] = 2 // OK
mySlice[2] = 3 // OK
// mySlice[3] = 4 // PANIC: индекс за пределами длины (3)
// mySlice[4] = 5 // PANIC: индекс за пределами длины (3)

fmt.Println(len(mySlice)) // 3
fmt.Println(cap(mySlice)) // 5

Практическое значение ёмкости среза

Ёмкость — это буфер для эффективного добавления элементов с помощью функции append.

  1. Пока количество элементов меньше ёмкости, append добавляет элемент в уже выделенную память базового массива, увеличивая только длину. Это очень быстрая операция.

    mySlice = append(mySlice, 4) // Длина станет 4, Ёмкость останется 5
    mySlice = append(mySlice, 5) // Длина станет 5, Ёмкость останется 5
    
  2. При попытке добавить элемент, когда длина равна ёмкости, происходит переаллокация: Go создаёт новый базовый массив (обычно удваивая ёмкость, но алгоритм может меняться), копирует в него все старые элементы, добавляет новый и возвращает срез, ссылающийся на этот новый массив. Эта операция дороже по производительности.

    mySlice = append(mySlice, 6) // Длина = 6, Ёмкость может стать 10 (удвоение)
    

Почему это важно?

  • Производительность: Предварительное выделение достаточной ёмкости (make([]T, 0, expectedSize)) для известного или ожидаемого количества элементов позволяет избежать многократных дорогостоящих переаллокаций при наполнении среза.
  • Оптимизация памяти: Слишком большой начальный capacity для маленьких срезов ведёт к неэффективному использованию памяти.
  • Поведение операций: Операции "ре-слайсинга" (slice[low:high]) создают новый срез, который делит базовый массив с оригинальным срезом. Его ёмкость будет считаться от первого элемента нового среза до конца базового массива.
    original := []int{1, 2, 3, 4, 5} // len=5, cap=5
    subSlice := original[1:3]        // [2, 3], len=2, cap=4!
    // subSlice может быть увеличен до 4 элементов без переаллокации,
    // но это повлияет на original[4].
    

Итог

  • Для массива: длина == ёмкость. Оба значения фиксированы и неизменны.
  • Для среза: длина <= ёмкость.
    *   **Длина** — это "видимая" часть, рабочая область среза.
    *   **Ёмкость** — это общий запас памяти "под капотом", который определяет, когда произойдёт дорогостоящее перевыделение памяти при росте среза.

Понимание этой разницы — обязательный навык для написания эффективного и корректного кода на Go, особенно при работе с большими или часто изменяемыми коллекциями данных.

В чём разница между длиной и ёмкостью массива? | PrepBro