数组
使用前需要先定义, 数组中只有有一个类型, 不允许有多个类型.
如果长度不确定,可以用[…]来接收长度不确定的数组
var 数组名[长度]类型 : var a1 [3]bool 定义一个3个长度布尔类型的数组
不设初始值,默认元素都是零值(布尔值:false, 整型和浮点型都是0, 字符串"")
package main
import "fmt"
//数组
// 存放元素的容器
// 必须指定存放的元素的类型和容量(长度)
// 数组的长度是数据类型的一部分
func main() {
var a1 [3]bool
var a2 [4]bool
fmt.Printf("a1:%T a2:%T", a1, a2)
// 数组的初始化
// 如果不初始化: 默认元素都是零值(布尔值:false, 整型和浮点型都是0, 字符串"")
fmt.Println(a1, a2)
// 1.初始化方式1
a1 = [3]bool{true, true, true}
fmt.Println(a1)
// 2. 初始化方式2
// ...根据初始值自动腿短数组的长度是多少
a10 := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println(a10) //[0 1 2 3 4 5 6 7]
// 3. 初始化方式3
a3 := [5]int{1, 2} // 后边的置为领值[1 2 0 0 0]
fmt.Println(a3)
a31 := [5]int{0: 1, 4: 2} //根据索引赋值, 没赋值的置为领值[1 0 0 0 2]
fmt.Println(a31)
// 数组的遍历
citys := [...]string{"杭州", "北京", "上海"}
// 根据索引遍历
for i := 0; i < len(citys); i++ {
fmt.Println(citys[i])
}
for _, city := range citys {
fmt.Println(city)
}
// 多维数组
var a11 [3][2]int
a11 = [3][2]int{
[2]int{1, 2},
[2]int{3, 4},
[2]int{5, 6},
}
fmt.Println(a11)
for _, v1 := range a11 {
fmt.Println(v1)
for _, v2 := range v1 {
fmt.Println(v2)
}
}
// 数组是值类型
b1 := [3]int{1, 2, 3} // [1 2 3]
b2 := b1 // [1 2 3]
b2[0] = 100 // [100 2 3]
fmt.Println(b1, b2)
// 作业, 求数组的和
c1 := [...]int{1, 3, 5, 7, 8}
total := 0
for _, i := range c1 {
total += i
}
fmt.Println(total)
// 找出数组中和为指定值的两个元素的下标
target := 8
for k, v := range c1 {
for a := k + 1; a < len(c1); a++ {
if v+c1[a] == target {
fmt.Printf("%d %d\t", k, a)
}
}
}
}
切片(slice)
切片是指向了一个底层的数组 切片的长度就是它元素的个数
切片的容量是底层数组从切片的第一个元素到最后一个元素的数量.(指的是切片的第一个元素在原数组中的位置到原数组最后一个位置的数量)
空的切片在被赋值的时候, 也是底层先创建 一个数组,在由这个数组进行切片
切片相当于PHP中的引用赋值
, 当原数组更改的时候, 从这个数组衍生出去的切片的值都会更改, 实际上切片中只是保存了底层数组的位置
package main
import "fmt"
func main() {
// 切片的定义
var s1 []int // 定义一个存放int类型元素的切片
var s2 []string // 定义一个存放字符串类型元素的切片
fmt.Println(s1, s2)
fmt.Println(s1 == nil) // true , 相当于没有开辟内存空间
fmt.Println(s2 == nil) // true
// 初始化
s1 = []int{1, 2, 3}
s2 = []string{"留下", "闲林", "西溪"}
fmt.Println(s1, s2)
fmt.Println(s1 == nil) // false
fmt.Println(s2 == nil) // false
// 长度和容量
fmt.Printf("len(s1):%d cap(s1):%d\n", len(s1), cap(s1)) //len(s1):3 cap(s1):3
fmt.Printf("len(s2):%d cap(s2):%d\n", len(s2), cap(s2)) //len(s2):3 cap(s2):3
// 2.由数组得到切片
a1 := [...]int{1, 3, 5, 7, 9, 11, 13}
a3 := a1[0:4] // 基于一个数组切割,左包含有不包含,左闭右开 [1 3 5 7]
fmt.Println(a3)
a4 := a1[1:6] // 冒号两边是索引值, 包含左边索引值,不包含右边索引值, 所以是切从1到5的所有值
fmt.Println(a4)
a5 := a1[:4] // -> [0:4]
a6 := a1[3:] // -> [3:len(a1)] [7 9 11 13]
a7 := a1[:] // -> [0:len(a1)] [1 3 5 7 9 11 13]
fmt.Println(a5, a6, a7)
// 切片的容量是指底层数组的容量
fmt.Printf("len(a5):%d cap(a5):%d\n", len(a5), cap(a5)) //len(a5):4 cap(a5):7
// 底层数组从切片的第一个元素到最后的元素数量
fmt.Printf("len(a6):%d cap(a6):%d\n", len(a6), cap(a6)) //len(a6):4 cap(a6):4
// 切片再切片
a8 := a6[3:]
fmt.Printf("len(a8):%d cap(a8):%d\n", len(a8), cap(a8)) //len(s8):1 cap(s8):1
a1[6] = 13000 // 修改了底层数组的值
fmt.Println("a6:", a6) //a6: [7 9 11 13000]
fmt.Println("a8:", a8) //a8: [13000]
}
make
make([]T, size, cap)
其中:
- T:切片的元素类型
- size:切片中元素的数量
- cap:切片的容量
切片的本质
切片就是一个框, 框住了一块连续的内存.
切片属于引用类型, 真正的数据保存在底层数组中.
要判断 一个切片是否是空的, 要用len(s) == 0
来判断
package main
import "fmt"
func main() {
// make() 函数创造切片
s1 := make([]int, 5, 10) // make(类型, 长度, 容量)
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1)) //s1=[0 0 0 0 0] len(s1)=5 cap(s1)=10
s2 := make([]int, 0, 10)
fmt.Printf("s2=%v len(s2)=%d cap(s2)=%d", s2, len(s2), cap(s2)) //s1=[0 0 0 0 0] len(s1)=5 cap(s1)=10
// 切片的赋值 , 共享内存地址, 修改一个,其他引用值都会跟着变
s3 := []int{1, 3, 5}
s4 := s3
fmt.Println(s3, s4) //[1 3 5] [1 3 5]
s3[0] = 1000
fmt.Println(s3, s4) //[1000 3 5] [1000 3 5]
// 切片的遍历
// 1.索引遍历
for i := 0; i < len(s3); i++ {
fmt.Println(s3[i])
}
// 1.索引遍历
for i, v := range s3 {
fmt.Println(i, v)
}
}
append
Go语言的内建函数append()可以为切片动态添加元素。 可以一次添加一个元素,可以添加多个元素,也可以添加另一个切片中的元素(后面加…)。
每个切片会指向一个底层数组,这个数组的容量够用就添加新增元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收append函数的返回值。
package main
import "fmt"
func main() {
a1 := []int{1, 3, 5}
a2 := a1
var a3 = make([]int, 3, 3)
copy(a3, a1) // copy 会把原先的内存复制到另外一个内存中,和原先内存不在一起,修改原先切片数据,把影响copy后的切片
fmt.Println(a1, a2, a3) //[1 3 5] [1 3 5] [1 3 5]
a1[0] = 100
fmt.Println(a1, a2, a3) //[100 3 5] [100 3 5] [1 3 5]
// 将a1中的索引为1的3这个元素删掉
a1 = append(a1[:1], a1[2:]...) // 取出前半部分和后半部分,拼接一下获取删除后的切片, 实际上是修改底层的数组对应索引的值
fmt.Println(a1) //[100 5]
fmt.Println(cap(a1)) //3
x1 := [...]int{1, 3, 5} // 数组
s1 := x1[:] // 切片
fmt.Println(s1, len(s1), cap(s1))
// 1.切片不保存具体的值
// 2切片对应一个底层数组
// 3.底层数组都是占用一块连续的内存
fmt.Printf("%p\n", &s1[0]) //0xc0000be0c0
s1 = append(s1[:1], s1[2:]...) // 相当于对切片重新赋值, 赋值后会覆盖底层数组对应索引的值
fmt.Printf("%p\n", &s1[0]) //0xc0000be0c0
fmt.Println(s1, len(s1), cap(s1)) //[1 5] 2 3
// 对切片的赋值,修改了底层数组的数据
fmt.Println(x1) //[1 5 5]
}
copy
Go语言内建的copy()
函数可以迅速地将一个切片的数据复制到另外一个切片空间
中,copy()
函数的使用格式如下:
copy(destSlice, srcSlice []T)
其中:
- srcSlice: 数据来源切片
- destSlice: 目标切片
具体看上边的案例
练习题
package main
import (
"fmt"
"sort"
)
func main() {
var a = make([]int, 5, 10)
for i := 0; i < 10; i++ {
a = append(a, i)
}
fmt.Println(a) // [0 0 0 0 0 0 1 2 3 4 5 6 7 8 9]
//sort排序
var a1 = [...]int{3, 7, 8, 9, 1}
sort.Ints(a1[:])
fmt.Println(a1) // [1 3 7 8 9]
}