Память и производительность срезов

┌─────────┬─────┬─────┐
│  *array │ len │ cap │
└─────────┴─────┴─────┘
     │
     ▼
┌────┬────┬────┬────┬────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │
└────┴────┴────┴────┴────┘
  • len — количество доступных элементов.
  • cap — размер выделенного массива от начала среза до конца.

Когда len == cap, append выделяет новый массив и копирует данные:

s := make([]int, 0)
for i := 0; i < 8; i++ {
    prev := cap(s)
    s = append(s, i)
    if cap(s) != prev {
        fmt.Printf("len=%d  cap: %d → %d\n", len(s), prev, cap(s))
    }
}
// cap: 0→1, 1→2, 2→4, 4→8

Стратегия удвоения даёт амортизированную стоимость O(1) на один append.

Если конечный размер известен — выделяй сразу:

// Плохо: многократное копирование
var result []int
for _, v := range source {
    result = append(result, transform(v))
}

// Хорошо: одна аллокация
result := make([]int, 0, len(source))
for _, v := range source {
    result = append(result, transform(v))
}
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 3)
n := copy(dst, src)  // копирует min(len(dst), len(src)) элементов
fmt.Println(dst, n)  // [1 2 3] 3

Подсрез держит ссылку на весь исходный массив:

// Утечка: большой массив не будет собран GC
data := readBigFile()
header := data[:10]  // data остаётся в памяти

// Исправление: явная копия
header := make([]byte, 10)
copy(header, data[:10])

ЗаданиеСложность
1Упражнение 1easy
2Упражнение 2easy
3Упражнение 3medium