Здесь, в посте, вы видите следующие подходы к объединению фрагментов в go.
- Обычно практикуется конкатенация двух слайсов
- Простое решение для объединения более двух фрагментов
- Эффективное решение для объединения более двух фрагментов
Обычно практикуется объединение двух фрагментов
package main
import "fmt"
func main() {
slice1 := []string{"apple", "banana", "peach" }
slice2 := []string{"avacado", "kiwi", "pineapple"}
slice3 := append(slice1, slice2...)
fmt.Println(slice3)
}
Конкатенацию срезов в Go можно выполнить с помощью встроенной функции append() из стандартной библиотеки.
Здесь, в приведенном выше примере, срез (slice1) является первым аргументом, а все элементы из второго среза (slice2) — вторым. используя функцию append(), когда мы передаем эти фрагменты в качестве аргументов, он возвращает обновленный фрагмент, содержащий все элементы.
Оператор с тремя точками (...) после второго аргумента в append() означает, что это функция с переменным числом аргументов и принимает неограниченное количество аргументов.
Простое решение для объединения более двух фрагментов
Существует ограничение на количество (2) слайсов, которые вы можете передать функции добавления, и предположим, что вы столкнулись с необходимостью добавить более двух слайсов, здесь ниже приведена одна из версий подхода к слиянию.
package main
import "fmt"
func main() {
slices := [][]string{{"apple", "banana", "peach"},
{"orange", "grape", "mango"},
{"strawberry", "blueberry", "raspberry"}}
fmt.Println(concatAppend(slices))
}
func concatAppend(slices [][]string) []string {
var tmp []string
for _, s := range slices {
tmp = append(tmp, s...)
}
return tmp
}
То, что мы делаем здесь, по сути, перебирает срезы и выполняет повторяющееся добавление к массиву tmp для сохранения каждой итерации.
Эффективное решение (в 2 раза быстрее) для объединения более двух фрагментов
Некоторые считают, что такой подход создания пустого фрагмента с последующим добавлением может привести к множеству ненужных выделений памяти, которых можно избежать и повысить производительность кода в 2 раза с помощью приведенного ниже подхода.
package main
import "fmt"
func main() {
slices := [][]string{{"apple", "banana", "peach"},
{"orange", "grape", "mango"},
{"strawberry", "blueberry", "raspberry"}}
fmt.Println(concatCopyPreAllocate(slices))
}
func concatCopyPreAllocate(slices [][]string) []string {
var totalLen int
for _, s := range slices {
totalLen += len(s)
}
tmp := make([]string, totalLen)
var i int
for _, s := range slices {
i += copy(tmp[i:], s)
}
return tmp
}
Вот ссылка на ответ Кэмерона Спарра на StackOverflow с примером бенчмаркинга.
Побочные эффекты объединения фрагментов с помощью append()
append() не всегда создает новый базовый массив для возвращаемого фрагмента. Если емкость фрагмента 1 достаточно велика для размещения элементов из фрагмента 2, результирующий фрагмент будет использовать тот же базовый массив, что и фрагмент 1. которые могут иметь непреднамеренные побочные эффекты.
Пример:
package main
import "fmt"
func main() {
slice1 := make([]int, 3, 6) // s1 has capacity of 5
slice1[0], slice1[1], slice1[2] = 1, 2, 3 // s1 has length of 3
slice2 := []int{4, 5, 6}
slice3 := append(slice1, slice2...)
fmt.Println("Before adding element to slice 3")
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
fmt.Println("slice3:", slice3)
//fmt.Println(slice1, slice3)
slice3[0] = 7
fmt.Println("After adding element to slice 3")
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
fmt.Println("slice3:", slice3)
}
//output
Before adding element to slice 3
slice1: [1 2 3]
slice2: [4 5 6]
slice3: [1 2 3 4 5 6]
After adding element to slice 3
slice1: [7 2 3]
slice2: [4 5 6]
slice3: [7 2 3 4 5 6]
Изменение значения элемента в фрагменте 3 также приводит к изменению фрагмента 1. Это произошло потому, что они оба используют один и тот же базовый массив, и это могло привести к ошибкам. Эту проблему можно предотвратить, убедившись, что срез, возвращаемый append(), поддерживается новым базовым массивом независимо от емкости слайса 1.
Вот как:
package main
import "fmt"
func main() {
slice1 := make([]int, 3, 6) // s1 has capacity of 5
slice1[0], slice1[1], slice1[2] = 1, 2, 3 // s1 has length of 3
slice2 := []int{4, 5, 6}
slice3 := append(slice1[:len(slice1):len(slice1)], slice2...)
fmt.Println("Before adding element to slice 3")
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
fmt.Println("slice3:", slice3)
//fmt.Println(slice1, slice3)
slice3[0] = 7
fmt.Println("After adding element to slice 3")
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
fmt.Println("slice3:", slice3)
}
//output
Before adding element to slice 3
slice1: [1 2 3]
slice2: [4 5 6]
slice3: [1 2 3 4 5 6]
After adding element to slice 3
slice1: [1 2 3]
slice2: [4 5 6]
slice3: [7 2 3 4 5 6]
Настройка функции добавления для
append(slice1[:len(slice1):len(slice1)], slice2...) //output Before adding element to slice 3 slice1: [1 2 3] slice2: [4 5 6] slice3: [1 2 3 4 5 6] After adding element to slice 3 slice1: [1 2 3] slice2: [4 5 6] slice3: [7 2 3 4 5 6]
Заключение
мы рассмотрели два подхода к объединению двух или более слайсов в Go, а также способы избежать побочного эффекта от использования функции append(). Если у вас есть дополнительные сведения о конкатенации слайсов в Go, оставьте комментарий.
Удачного кодирования!
Повышение уровня кодирования
Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:
- 👏 Хлопайте за историю и подписывайтесь на автора 👉
- 📰 Смотрите больше контента в публикации Level Up Coding
- 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"
🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу