Функция append
в Go используется для добавления элементов в срезы (slices). Её сигнатура:
func append(slice []Type, elems ...Type) []Type
nums := []int{1, 2, 3}
nums = append(nums, 4) // [1, 2, 3, 4]
Когда длина (length) превышает ёмкость (capacity), append:
Правило роста capacity (до Go 1.18):
С Go 1.18 алгоритм стал сложнее:
newcap := oldcap
doublecap := newcap + newcap
if newLen > doublecap {
newcap = newLen
} else {
const threshold = 256
if oldcap < threshold {
newcap = doublecap
} else {
for newcap < newLen {
newcap += (newcap + 3*threshold) / 4
}
}
}
Пример с разными сценариями:
a := make([]int, 2, 4) // length=2, capacity=4
b := append(a, 1) // Использует существующий буфер
c := append(a, 1, 2, 3) // Требуется переаллокация
s1 := []int{1, 2}
s2 := append(s1, 3)
s1[0] = 9
// s1: [9, 2], s2: [1, 2, 3] или [9, 2, 3] в зависимости от capacity
Используется оператор ...
:
a := []int{1, 2}
b := []int{3, 4}
a = append(a, b...) // [1, 2, 3, 4]
Предварительное выделение capacity:
// Плохо: множественные переаллокации
var s []int
for i := 0; i < 1000; i++ {
s = append(s, i)
}
// Хорошо: предварительное выделение
s := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
s = append(s, i)
}
var s []int
s = append(s, 1) // Работает корректно
a := []int{1, 2, 3}
b := append(a[:2], 4)
// Изменение b может повлиять на a
Гонки данных при параллельном использовании:
Утечки памяти:
append в Go — это мощный механизм работы со срезами, который автоматически управляет выделением памяти, но требует понимания его внутренней работы для эффективного использования и избежания распространённых ошибок, связанных с аллокацией и разделением памяти.