数组
什么是数组
这是一个古老的数据结构概念了:
有序、同类、定长的集合就是数组。
如何定义?
最标准的:
var arry_a [3]int = [3]int{1,2,3}
其他的
var arry_a [3]int //这种情况下,arry_a的三个元素将被赋默认零值
var arry_a [3]int =[3]int{1,2}//这种情况下,arry_a[2]将被赋默认零值
arry_a := [...]int{1,2,3}
arry_a := [3]int{1,2,3}
* 注意[3]int与[4]int是不同类型,不能把[4]int类型的值赋给[3]int类型的变量
* 强调一点,golang里面只有值传递,没有引用传递。所谓值传递可以看成暴力的内存拷贝。这一点在数组传值上体现地特别明显!
package main
import "fmt"
func main() {
a := [3]int{1, 2, 3}
b := a
b[0] = 100
fmt.Println(a[0])
fmt.Println(b[0])
}
输出
1
100
如何遍历?
通常是通过关键字range,也可以按下标遍历
package main
import "fmt"
func main() {
a := [3]int{1, 2, 3}
for value := range a {
fmt.Println(value)
}
fmt.Println("----------------")
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
}
输出
0
1
2
----------------
1
2
3
关于==和!=
package main
import "fmt"
func main() {
a := [3]int{1, 2, 3}
a1 := [3]int{1, 2, 3}
fmt.Println(a == a1)
fmt.Println(a != a1)
a2 := [3]int{1, 3, 2}
fmt.Println(a == a2)
fmt.Println(a != a2)
}
结果
true
false
false
true
这说明:当且仅当数组元素全部对应相等时,两个数组才相等,否则不等。
* 注意,由于[3]int与[4]int是不同类型,所以其值不可比较(会导致语法错误)!
动态数组——切片
package main
import "fmt"
func main() {
var sp_int = make([]int, 0)
fmt.Println(len(sp_int))
sp_int = append(sp_int, 1, 2, 3)
fmt.Println(len(sp_int))
sp_int_copy1 := append(sp_int, 4)
fmt.Println(len(sp_int))
fmt.Println(len(sp_int_copy1))
sp_int_copy1[0] = 100
fmt.Println(sp_int)
fmt.Println(sp_int_copy1)
sp_int_copy2 := sp_int
sp_int_copy2[1] = 200
fmt.Println(sp_int_copy1)
fmt.Println(sp_int_copy2)
var sp_int_copy3 = make([]int, 3)
copy(sp_int_copy3, sp_int)
sp_int_copy3[2] = 300
fmt.Println(sp_int)
fmt.Println(sp_int_copy3)
}
0
3
3
4
[100 2 3]
[100 2 3 4]
[100 200 3 4]
[100 200 3]
[100 200 3]
[100 200 300]
可以看到数组之间的赋值是引用赋值,其实所有new,make创建的对象都是引用赋值,因为他们创建出来的实际上是指针(这个C/C++的new很相似)。
另外,切片可定义最大长度:
test:= make([]int,3,5) //切片test初始长度是3,最长可扩充到5,用cap(test)可以得到test切片的最大长度
有趣的切片拼接
切片拼接用内建append函数,append有两种用法:
用法1:将单个元素拼接到切片 append(切片,元素1,元素2,...,元素n),这种用法第一个参数是需要拼接的切片,后面可以写不定数目的同类型元素。这些元素将按书写顺序拼接到切片上。
用法2:将另一个切片拼接到切片上append(切片,另一个切片...),这种用法要注意其语法现象,另一个切切片后要加三个点,声明所要拼接的是切片而不是单个元素。注意,切片一个一个地拼接,也就是说这种用法只能传入两个参数:带拼接切片,另一个切片。
无论是哪种用法,只要拼接没有超过原切片的容量,append将返回一个地址相同的新切片。新旧切片唯一的区别仅在于长度不同(地址,容量均相同)
切片拼接还有一个有趣地地方就是容量问题:当原切片容量到达极限仍容纳不下拼接对象后,append函数会返回一个新的切片(地址),原切片被原样保留,举例如下:
package main
import "fmt"
func main() {
a := make([]int, 0, 10)
b := append(a, 1, 2, 3)
fmt.Println(len(a), cap(a), "-->", a)
fmt.Printf("a-->%p\n", a)
fmt.Println(len(b), cap(b), "-->", b)
fmt.Printf("b-->%p\n", b)
b = append(b, []int{4, 5, 6}...)
fmt.Println(len(b), cap(b), "-->", b)
fmt.Printf("b-->%p\n", b)
fmt.Println("--------------------------------------")
c := append(b, []int{7, 8, 9, 10, 11}...)
fmt.Println(len(b), cap(b), "-->", b)
fmt.Printf("b-->%p\n", b)
fmt.Println(len(c), cap(c), "-->", c)
fmt.Printf("c-->%p\n", c)
}
0 10 --> []
a-->0xc0000a6000
3 10 --> [1 2 3]
b-->0xc0000a6000
6 10 --> [1 2 3 4 5 6]
b-->0xc0000a6000
--------------------------------------
6 10 --> [1 2 3 4 5 6]
b-->0xc0000a6000
11 20 --> [1 2 3 4 5 6 7 8 9 10 11]
c-->0xc0000b0000
切片移除
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(len(a), cap(a), "-->", a)
del_index := 3
b := append(a[:del_index], a[del_index+1:]...)
fmt.Println(len(b), cap(b), "-->", b)
}
9 9 --> [1 2 3 4 5 6 7 8 9]
8 9 --> [1 2 3 5 6 7 8 9]
数组和切片的区别
最显著的区别是,数组是值类型,切片是指针类型(实际上所有通过make和new创建的变量都是指针类型)
package main
import (
"fmt"
"reflect"
)
func main() {
test1 := make([]int, 3)
test1[0] = 1
test1[1] = 2
test1[2] = 3
test2 := []int{1, 2, 3}
test3 := [...]int{1, 2, 3}
test11 := test1
test11[0] = 123
test22 := test2
test22[0] = 123
test33 := test3
test33[0] = 123
fmt.Println(test1, reflect.TypeOf(test1))
fmt.Println(test2, reflect.TypeOf(test2))
fmt.Println(test3, reflect.TypeOf(test3))
}
[123 2 3] []int
[123 2 3] []int
[1 2 3] [3]int
注意:test2也是切片类型,只是写法不同而已
如果需要将数组转换成切片:
a := [3]int{1, 2, 3} b := a[:] fmt.Println(reflect.TypeOf(a).String()) fmt.Println(reflect.TypeOf(b).String())
[3]int
[]int
关于make和new关键字
说起make就不得不说另一个关键字:new
package main
import (
"fmt"
"reflect"
"strconv"
)
func main() {
a := new([3]int)
a[0] = 100
a[1] = 200
a[2] = 300
b := make([]int, 3)
fmt.Println(reflect.TypeOf(a).String() + "-->" + strconv.Itoa(len(a)))
fmt.Println(reflect.TypeOf(b).String() + "-->" + strconv.Itoa(len(b)))
for i := 0; i < len(a); i++ {
fmt.Print(strconv.Itoa(a[i]) + " ")
}
fmt.Print("\n")
for i := 0; i < len(a); i++ {
fmt.Print(strconv.Itoa(b[i]) + " ")
}
fmt.Println("\n---------------------------------")
for index, val := range a {
fmt.Print(strconv.Itoa(index) + "->" + strconv.Itoa(val) + " ")
}
fmt.Print("\n")
for index, val := range b {
fmt.Print(strconv.Itoa(index) + "->" + strconv.Itoa(val) + " ")
}
fmt.Print("\n")
aa := a
aa[0] = 1000
fmt.Println(a)
}
输出
*[3]int-->3
[]int-->3
100 200 300
0 0 0
---------------------------------
0->100 1->200 2->300
0->0 1->0 2->0
&[1000 200 300]
make只能建立slice,map和channel三种类型;new没有限制。
有趣的数组截取(注意语法现象)
func main() {
a := make([]int, 4)
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
b := a[2:]
fmt.Println(b)
c := new([4]int)
c[0] = 10
c[1] = 20
c[2] = 30
c[3] = 40
d := c[2:]
fmt.Println(d)
}
[3 4]
[30 40]