Part 2: Working with Data Structures
CHAPTER 6: Slicing and Dicing Using Arrays and Slices
В Go, массив (array) это пронумерованная последовательность элементов одного типа указанной длины
После создания массив не может изменить длину
Для описания массива используется следующий синтаксис
var array_name [size_of_array]data_type
? cat main.go
package main
import "fmt"
func main() {
var nums [5]int
fmt.Println(nums)
}
? /usr/local/go/bin/go run main.go
[0 0 0 0 0]
Массив содержит точно указанное кол-во элементов (в примере выше указано 5, значит 6 элементов в этот конкретный массив не влезет)
Счет элементов в массиве начинается с 0, то есть первый элемент в примере выше доступен по индексу 0, а последний по индексу 4? cat main.go package main import "fmt" func main() { var nums [5]int = [5]int{1, 2, 3, 4, 5} fmt.Println(nums) fmt.Println(nums[0]) fmt.Println(nums[4]) }
? /usr/local/go/bin/go run main.go [1 2 3 4 5] 1 5
Можно сразу инициализировать его значениями
nums := [5]int{1, 2, 3, 4, 5}
При такой инициализации можно опустить длину массива поставив вместо нее многоточие
? cat main.go
package main
import "fmt"
func main() {
nums := [...]int{1, 2, 3, 4, 5}
fmt.Println(nums)
fmt.Println(nums[0])
fmt.Println(nums[4])
}
? /usr/local/go/bin/go run main.go
[1 2 3 4 5]
1
5
Получить длину массива можно с помощью функции len()
nums := [...]int{1, 2, 3, 4, 5}
fmt.Println(nums)
fmt.Println(len(nums))
? /usr/local/go/bin/go run main.go
[1 2 3 4 5]
5
Показанные выше массивы - одномерные
Далее рассмотрим многомерные массивы
Двухмерный массив:
? cat main.go
package main
import "fmt"
import "strconv"
func main() {
var table [5][6]string
for row := 0; row < 5; row++ {
for column := 0; column < 6; column++ {
table[row][column] = strconv.Itoa(row) + "," + strconv.Itoa(column)
}
}
for row := 0; row < 5; row++ {
fmt.Println(table[row])
}
}
? /usr/local/go/bin/go run main.go
[0,0 0,1 0,2 0,3 0,4 0,5]
[1,0 1,1 1,2 1,3 1,4 1,5]
[2,0 2,1 2,2 2,3 2,4 2,5]
[3,0 3,1 3,2 3,3 3,4 3,5]
[4,0 4,1 4,2 4,3 4,4 4,5]
В примере выше можно неправильно посчитать элементы, нужно обратить внимание на то что разделителем в строках является пробел, а не запятая
Трехмерный массив:
? cat main.go
package main
import "fmt"
import "strconv"
func main() {
var cube [4][3][3]string
for row := 0; row < 4; row++ {
for column := 0; column < 3; column++ {
for depth := 0; depth < 3; depth++ {
cube[row][column][depth] = strconv.Itoa(row) +
strconv.Itoa(column) + strconv.Itoa(depth)
}
}
}
for row := 0; row < 4; row++ {
for column := 0; column < 3; column++ {
fmt.Println(cube[row][column])
}
fmt.Println()
}
}
? /usr/local/go/bin/go run main.go
[000 001 002]
[010 011 012]
[020 021 022]
[100 101 102]
[110 111 112]
[120 121 122]
[200 201 202]
[210 211 212]
[220 221 222]
[300 301 302]
[310 311 312]
[320 321 322]
Слайсы это тоже самое что и массивы, но в отличие от массивов они могут изменять свой размер
Создать слайс можно с помощью функции make()
? cat main.go
package main
import "fmt"
func main() {
s := make([]int, 5)
fmt.Println(s)
}
? /usr/local/go/bin/go run main.go
[0 0 0 0 0]
Очень похоже на массив
Но технически слайс это вид (view) на нижележащий массив
У слайса есть хедер который содержит три поля:
- ptr - указатель на область памяти нижележащего массива
- len - длина слайса
- cap - (capacity) максимальный размер массива
Функции len()
и cap()
покажут длину и вместительность слайса
? cat main.go
package main
import "fmt"
func main() {
s := make([]int, 5)
fmt.Println(s)
fmt.Println(len(s))
fmt.Println(cap(s))
}
? /usr/local/go/bin/go run main.go
[0 0 0 0 0]
5
5
В примере выше у нас кол-во элементов и вместительность равны
Но можно явно задать эти значения при создании слайса
Нужно передать в функцию make()
нужные аргументы
s := make([]int, 5, 10)
? /usr/local/go/bin/go run main.go
[0 0 0 0 0]
5
10
? cat main.go
package main
import "fmt"
import "reflect"
func main() {
s := make([]int, 5, 10)
a := []int{1, 2, 3}
a2 := [3]int{1, 2, 3}
fmt.Println(reflect.TypeOf(s), len(s), cap(s))
fmt.Println(reflect.TypeOf(a), len(a), cap(a))
fmt.Println(reflect.TypeOf(a2), len(a2), cap(a2))
}
? /usr/local/go/bin/go run main.go
[]int 5 10
[]int 3 3
[3]int 3 3
Добавить элементы в слайс можно с помощью функции append()
? cat -n main.go
1 package main
2
3 import "fmt"
4 import "reflect"
5
6 func main() {
7 s := make([]int, 5, 10)
8 a := []int{1, 2, 3}
9 a2 := [3]int{1, 2, 3}
10 a3 := [...]int{1, 2, 3}
11 fmt.Println(reflect.TypeOf(s), len(s), cap(s))
12 fmt.Println(reflect.TypeOf(a), len(a), cap(a))
13 fmt.Println(reflect.TypeOf(a2), len(a2), cap(a2))
14 fmt.Println(reflect.TypeOf(a3), len(a3), cap(a3))
15 fmt.Println()
16 s = append(s, 2, 3, 4)
17 a = append(a, 2, 3, 4)
18 a2 = append(a2, 2, 3, 4)
19 a3 = append(a3, 2, 3, 4)
20 fmt.Println(reflect.TypeOf(s), len(s), cap(s))
21 fmt.Println(reflect.TypeOf(a), len(a), cap(a))
22 fmt.Println(reflect.TypeOf(a2), len(a2), cap(a2))
23 fmt.Println(reflect.TypeOf(a3), len(a3), cap(a3))
24 }
? /usr/local/go/bin/go run main.go
# command-line-arguments
./main.go:18:16: first argument to append must be slice; have [3]int
./main.go:19:16: first argument to append must be slice; have [3]int
В примере выше видно что a2
и a3
не являются слайсами
Потому что многоточие в квадратных скобках подменяют собой кол-во элементов из фигурных скобок
А когда у массива в квадратных скобках пусто - это слайс
Корректный пример работы функции append()
:
? cat main.go
package main
import "fmt"
import "reflect"
func main() {
s := make([]int, 2, 5)
fmt.Println("type, len, cap:", reflect.TypeOf(s), len(s), cap(s))
fmt.Println("0 item mem addr:", &s[0])
fmt.Println(s)
fmt.Println()
s = append(s, 2, 3, 4)
fmt.Println("type, len, cap:", reflect.TypeOf(s), len(s), cap(s))
fmt.Println("0 item mem addr:", &s[0])
fmt.Println(s)
fmt.Println()
s = append(s, 2, 3, 4)
fmt.Println("type, len, cap:", reflect.TypeOf(s), len(s), cap(s))
fmt.Println("0 item mem addr:", &s[0])
fmt.Println(s)
}
? /usr/local/go/bin/go run main.go
type, len, cap: []int 2 5
0 item mem addr: 0xc000018180
[0 0]
type, len, cap: []int 5 5
0 item mem addr: 0xc000018180
[0 0 2 3 4]
type, len, cap: []int 8 10
0 item mem addr: 0xc00001e050
[0 0 2 3 4 2 3 4]
Как видим при превышении вместимости слайса, его вместимость увеличилась
Это произошло при помощи копирования старого нижележащего массива в новый с большей емкостью (то что нижележащий массив изменился видно по адресу памяти первого элемента слайса)
Заранее указать большую вместимость слайса может быть нужно если ты знаешь что придется добавить какое-то количество значений в слайс и хочешь избежать оверхеда на пересоздание нижележащего массива
Замеры:
(yc-test:argocd) vandud@macbook: golang-study ? cat main.go
package main
import "fmt"
import "time"
func main() {
tstart := time.Now().Unix()
s := make([]int, 1)
prevcap := 1
for i := 0; i < 500000000; i++ {
s = append(s, i)
if cap(s) != prevcap {
prevcap = cap(s)
fmt.Println(prevcap)
}
}
tend := time.Now().Unix()
fmt.Println(tend - tstart)
}
? /usr/local/go/bin/go run main.go
2
4
8
16
32
64
128
256
512
1024
1280
1696
2304
...
376559616
470700032
588375040
32
То есть работа такой программы заняла 32 секунды
А если мы в функцию make()
заранее зададим вместимость
s := make([]int, 1, 500000000)
То получим x2 по скорости
? /usr/local/go/bin/go run main.go
500000000
625000448
15
No Comments