Материал проходит вычитку — возможны неточности.
Урок 3 из 118

append и рост

Сергей поставил импорт на поток: сто тысяч файлов за один заход. Код на этом пакете то и дело тормозил, и Миша долго искал причину. Виновником оказался сам список: он рос по одному элементу и на каждом шаге перестраивался.

Главная мысль

append дописывает в конец слайса и при нехватке ёмкости выделяет новый массив — поэтому результат обязательно присваивать обратно, а известный заранее размер лучше закладывать в make.

Рост и переаллокация

Go

Если в подлежащем массиве хватает места, append пишет в него. Если нет — выделяет новый, большего размера, копирует старые элементы и дописывает новые.

Результат присваивают обратно

Именно поэтому идиома — s = append(s, x). append может вернуть слайс, указывающий на другой массив, а старый заголовок останется с прежними len и cap. Результат нельзя выбрасывать: присваивай его обратно, возвращай из функции или сохраняй в другую переменную. Потеряешь возврат — потеряешь новые элементы.

Коэффициент роста — деталь реализации: маленькие слайсы растут примерно вдвое, с ростом размера коэффициент плавно снижается. Полагаться на точные цифры не стоит — они менялись между версиями. Важно поведение: рост амортизированный, append в цикле — O(1) в среднем, но с редкими скачками на переаллокацию и копирование.

Prealloc

Когда размер известен заранее, выделяй с нужной ёмкостью, чтобы не переаллоцировать по многу раз:

func fill(n int) []int {
    out := make([]int, 0, n) // ёмкость задана, переаллокаций не будет
    for i := 1; i <= n; i++ {
        out = append(out, i)
    }
    return out
}
В следующем уроке

Вы узнаете:

  • как дописать весь слайс разом через append(s, slice...)
  • почему старый массив после переаллокации становится мусором
Задание · вопросы

Проверьте себя

Вопрос 1 из 2

Пакетный импорт знает число файлов заранее. Что происходит с числом выделений массивов при n = 1000: fill с запасом make([]int, 0, 1000) против «ленивого» fillGrow без запаса?

Вопрос 2 из 2

Что выведет a := make([]int, 1, 1); a[0] = 1; a = append(a, a...); fmt.Println(a)?

Итоги

append растёт за счёт переаллокаций; результат нельзя выбрасывать — присваивай, возвращай или сохраняй; заранее известный размер закладывай в make.

Наш сайт собирает куки. Чтобы тебе было удобнее им пользоваться. Нажимая на кнопку, ты даёшь разрешение собирать куки