Ответ
Вставка элементов в слайс в Go выполняется с помощью встроенной функции append
. Эта функция добавляет один или несколько элементов в конец слайса и возвращает новый слайс.
Основной механизм: append
append
проверяет capacity
(емкость) исходного слайса. Емкость — это размер нижележащего массива, на который ссылается слайс.
- Если
capacity
достаточно: Новый элемент добавляется в тот же самый нижележащий массив. Длина (len
) слайса увеличивается, ноcapacity
остается прежней. Возвращается новый заголовок слайса, указывающий на тот же массив, но с новой длиной. - Если
capacity
недостаточно: Go выделяет новый, больший массив. Все элементы из старого массива копируются в новый, после чего добавляется новый элемент. Затем возвращается заголовок слайса, который указывает уже на этот новый массив.
Стратегия роста capacity
Когда требуется новый массив, его емкость увеличивается по определенному алгоритму для амортизации затрат на выделение памяти:
- Для слайсов длиной до 1024 элементов,
capacity
обычно удваивается. - Для более крупных слайсов
capacity
растет медленнее, примерно на 25% (множитель 1.25). Это предотвращает чрезмерное расходование памяти на очень больших слайсах.
Примеры
Простое добавление в конец:
s := make([]int, 0, 2) // len=0, cap=2
s = append(s, 1) // len=1, cap=2. Места хватило.
s = append(s, 2) // len=2, cap=2. Места хватило.
s = append(s, 3) // len=3, cap=4. Места не хватило, создан новый массив с cap=4.
Вставка в середину:
Для вставки в середину используется комбинация append
и срезов. Это неэффективная операция, так как требует создания нового среза и копирования элементов.
s := []int{1, 2, 5}
// Вставить число 3 и 4 на позицию с индексом 2
insert := []int{3, 4}
s = append(s[:2], append(insert, s[2:]...)...)
// Результат: [1, 2, 3, 4, 5]
Важные нюансы
append
возвращает новый слайс. Всегда нужно использовать присваивание:s = append(s, ...)
, иначе можно потерять результат, если был выделен новый массив.-
Неочевидное изменение данных. Если у нескольких слайсов общий нижележащий массив и
append
не вызывает переаллокации, изменение одного слайса может затронуть другой.s1 := []int{1, 2, 3, 4} s2 := s1[:2] // s2: [1, 2], но смотрит на тот же массив s2 = append(s2, 99) // Места в capacity хватает! // s2 теперь [1, 2, 99] // s1 неожиданно тоже изменился: [1, 2, 99, 4]