06-Go语言基础之数组切片
Go数组Array
数组是
固定长度的相同类型元素组成的序列。一个数字由零或多个元素组成。
数组在声明时就确定了长度,成员允许修改,但是长度无法变化。
数组的长度是固定,因此Go更常用Slice(切片,动态增长或收缩序列)。
数组是值类型,用
索引下标访问每个元素,范围是0~len(数组)-1,访问越界会panic异常注意:赋值和传参是复制整个数组而不是指针
注意事项
数组的长度必须是常量,并且长度是数组类型的一部分.
数组支持索引访问
a[1]、c[7],索引的合法范围:0~len(array)-1,不支持负数索引。
array定义语法
package main
import "fmt"
/*
语法 var 数组变量 [元素数量]Type
比如:var a [5]int, 数组的长度必须是常量,并且长度是数组类型的一部分。一旦定义,长度不能变。
[5]int和[10]int是不同的类型。
var a [3]int
var b [4]int
a = b //不可以这样做,因为此时a和b是不同的类型
数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1,访问越界(下标在合法范围之外),则触发访问越界,会panic。
*/
var students [3]string
var isUp [5]bool
var score [3]int
func main() {
fmt.Printf("类型:%T 值:%#v\n", students, students)
fmt.Printf("类型:%T 值:%#v\n", isUp, isUp)
fmt.Printf("类型:%T 值:%#v\n", score, score)
}
/*
给人类看的数据
类型:[3]string 值:[ ]
类型:[5]bool 值:[false false false false false]
类型:[3]int 值:[0 0 0]
给机器看的数据
类型:[3]string 值:[3]string{"", "", ""}
类型:[5]bool 值:[5]bool{false, false, false, false, false}
类型:[3]int 值:[3]int{0, 0, 0}
*/
语法练习
package main
import "fmt"
func main() {
// 数组定义语法1
//var initArray [3]int // 长度为3个元素,类型是int的数组,int默认值是0
//
//var numArray [4]int = [4]int{4, 5, 6} //定义数组且赋值 ,可以简写,省去类型,不够的元素,自动补充默认值
//
//var stuArray = [3]string{"张飞", "刘备", "关羽"}
//
//fmt.Printf("类型:%T ,值:%v\n", initArray, initArray)
//fmt.Printf("类型:%T ,值:%v\n", numArray, numArray)
//fmt.Printf("类型:%T ,值:%v\n", stuArray, stuArray)
// 语法二
// 上述数组都是提前指定了固定的数组长度,赋值也必须和定义时的长度一致,否则会报错
// go数组也支持,让编译器自动判断元素长度
//var initArray [3]int // 不赋值的话,还是得写死数组元素长度
//
//var numArray = [...]int{4, 5, 6, 7, 8, 9} //定义数组且赋值 ,可以简写,省去类型,自动判断元素个数
//
//var stuArray = [...]string{"张飞", "刘备", "关羽", "孙二娘"}
//fmt.Printf("类型:%T ,值:%v\n", initArray, initArray)
//fmt.Printf("类型:%T ,值:%v\n", numArray, numArray)
//fmt.Printf("类型:%T ,值:%v\n", stuArray, stuArray)
// 语法三,通过索引,初始化元素值,以及简短变量
numArray2 := [...]int{1: 3, 3: 7} // 最大索引是3,也就是0,1,2,3
fmt.Printf("类型:%T ,值:%v\n", numArray2, numArray2) // 类型:[4]int ,值:[0 3 0 7]
}
/*
类型:[3]int ,值:[0 0 0]
类型:[6]int ,值:[4 5 6 7 8 9]
类型:[4]string ,值:[张飞 刘备 关羽 孙二娘]
*/
数组遍历
有如下几种语法
package main
import "fmt"
func main() {
var a1 = [...]int{1, 3, 5, 7}
//通过索引取值
for i := 0; i < len(a1); i++ {
fmt.Println(a1[i]) //根据索引依次获取数组每一个元素
}
//for循环遍历数组,索引和值,index可以省略用占位符_
for index, value := range a1 {
fmt.Println(index, value)
}
// 取出索引
for index := range a1 {
fmt.Println("索引:", index)
}
}
数组使用细节
package main
import "fmt"
func main() {
//数组是相同类型的多个元素,长度固定,无法扩容
var a1 [3]int //初始化数组 [0,0,0]
a1[1] = 7
a1[0] = 4
a1[2] = 1
fmt.Printf("类型:%T ,值:%v\n", a1, a1) // 类型:[3]int ,值:[4 7 1]
// 赋值必须和int类型对应
// a1[2]=77.1 错误
// 不得超出长度
//a1[3]=5
}
数组是值类型(后面再说)
- 声明数组
- 给数组元素赋值
- 使用数组
- 数组索引从0开始,且不得越界否则panic
- Go数组是值类型,变量传递默认是值传递,因此会进行值拷贝
- 修改原本的数组,可以使用引用传递(指针),避免数据复制
package main
import (
"fmt"
)
// 函数接收值类型,默认有值拷贝
func test(arr [3]int) {
arr[0] = 66
}
// 函数修改原本数组,需要使用引用传递
func test2(arr *[3]int) {
(*arr)[0] = 66 //可以缩写arr[0]=66 编译器自动识别,arr是指针类型
}
func main() {
//声明arr数组,需要考虑传递函数参数时,数组的长度一致性
arr := [3]int{11, 22, 33}
//test函数不会修改数组
test(arr)
fmt.Println(arr)
//test2修改了数组
test2(&arr)
fmt.Println(arr)
}
指针概念
指针数组: 每一个元素为指针类型的数组
数组指针: 获取变量数组变量的地址
[n]*T 指针数组 (每一个元素为指针类型)
*[n]T 数组指针 (获取数组的内存地址)
多维数组
Go语言支持多维数组,如二维数组(数组里又嵌套数组)
package main
import "fmt"
func main() {
// 外层是3个元素的数组,每一个元素是2个元素的数组
multiArray := [3][2]string{
{"张三", "李四"},
{"二狗", "胖蛋"},
{"石头", "大狗"},
}
fmt.Printf("类型:%T, 值:%v\n", multiArray, multiArray)
fmt.Printf("胖蛋出列:%v\n", multiArray[1][1])
fmt.Printf("石头出列:%v\n", multiArray[2][0])
fmt.Printf("李四出列:%v\n", multiArray[0][1])
}
/*
类型:[3][2]string, 值:[[张三 李四] [二狗 胖蛋] [石头 大狗]]
胖蛋出列:胖蛋
石头出列:石头
李四出列:李四
*/
二维数组遍历
for range
package main
import "fmt"
func main() {
// 外层是3个元素的数组,每一个元素是2个元素的数组
multiArray := [3][2]string{
{"张三", "李四"},
{"二狗", "胖蛋"},
{"石头", "大狗"},
}
for i, v := range multiArray {
fmt.Printf("i--%v,v--%v\n", i, v)
//遍历内层数组
for i2, v2 := range v {
fmt.Printf("v--%v , i2--%v ,v2--%v\n", v, i2, v2)
}
}
}
/*
i--0,v--[张三 李四]
v--[张三 李四] , i2--0 ,v2--张三
v--[张三 李四] , i2--1 ,v2--李四
i--1,v--[二狗 胖蛋]
v--[二狗 胖蛋] , i2--0 ,v2--二狗
v--[二狗 胖蛋] , i2--1 ,v2--胖蛋
i--2,v--[石头 大狗]
v--[石头 大狗] , i2--0 ,v2--石头
v--[石头 大狗] , i2--1 ,v2--大狗
*/
索引遍历
package main
import "fmt"
func main() {
// 外层是3个元素的数组,每一个元素是2个元素的数组
// 只有外层的数组,支持自动推导长度
multiArray := [...][2]string{
{"张三", "李四"},
{"二狗", "胖蛋"},
{"石头", "大狗"},
}
for i := 0; i < len(multiArray); i++ {
//fmt.Printf("索引:%v ,值:%v\n", i, multiArray[i])
//内层遍历
tmp := multiArray[i]
for i2 := 0; i2 < len(tmp); i2++ {
fmt.Printf("%v --- 索引:%v ,值:%v\n", tmp, i2, tmp[i2])
}
}
}
/*
[张三 李四] --- 索引:0 ,值:张三
[张三 李四] --- 索引:1 ,值:李四
[二狗 胖蛋] --- 索引:0 ,值:二狗
[二狗 胖蛋] --- 索引:1 ,值:胖蛋
[石头 大狗] --- 索引:0 ,值:石头
[石头 大狗] --- 索引:1 ,值:大狗
*/
数组练习题
package main
import "fmt"
// 求数组 [2,4,6,8] 所有元素的和
func arraySum() {
a1 := [...]int{2, 4, 6, 8, 10}
sum := 0
for _, v := range a1 {
sum += v
}
fmt.Println("数组元素之和:", sum)
}
// 从数组 [1,3,5,7,8] 中找出 两元素之和为8的 下标,打印出如(0,3) , (1,2)
func arraySum2() {
a2 := [...]int{1, 3, 5, 7, 8}
// 嵌套循环,计算两数之和
for i := 0; i < len(a2); i++ {
for j := 0; j < len(a2); j++ {
if a2[i]+a2[j] == 8 {
fmt.Printf("找到啦:(%d,%d)", i, j)
}
}
}
}
func main() {
arraySum2() // (0,3)(1,2)(2,1)(3,0)
}
Go切片slice
- Go语言切片(Slice)
- 切片是
可动态变化的序列,是对数组的引用,引用类型,遵循引用传递的机制 - slice类型写作[ ]T,T是slice元素类型,
var s1 []int,s1就是切片变量
slice前言
go数组长度固定,有较多局限性。
package main
import "fmt"
// 一个计算数组元素之和的函数
func arraySum(a [3]int) int {
sum := 0
for _, v := range a {
sum += v
}
return sum
}
func main() {
a := [3]int{1, 2, 3} //函数接收的数组,类型是固定死了,必须对应
res := arraySum(a)
fmt.Println("结果:", res)
}
// 但是我希望你这个函数,能随意接受我传入的不同长度的数组,是不是强大些。
切片是什么
- 切片slice是一个
拥有相同类型元素、可变长度的序列 - slice是基于array类型做的一层封装,灵活、可以自动扩容。
- slice是引用类型,内部结构包括、内存地址、长度、容量。
package main
import "fmt"
func main() {
//各种类型slice初始化
// 声明切片类型
var a []string //声明一个字符串切片
var b = []int{} //声明一个整型切片并初始化
var c = []bool{false, true} //声明一个布尔切片并初始化
var d = []bool{false, true} //声明一个布尔切片并初始化
fmt.Printf("类型:%T 、值:%v\n", a, a)
fmt.Printf("类型:%T 、值:%v\n", b, b)
fmt.Printf("类型:%T 、值:%v\n", c, c)
fmt.Printf("类型:%T 、值:%v\n", d, d)
// 注意切片是引用类型,不支持直接比较值,如 c==d
//数组是值类型,允许比较,如
array1 := [...]int{1, 2, 3}
array2 := [...]int{1, 2, 4} //注意数组的元素个数得一样, [3]int 和[4]int 是两种类型,无法比较!!!
if array1 == array2 {
fmt.Println("俩数组相等")
} else {
fmt.Println("俩数组值不等!!")
}
}
/*
类型:[]string 、值:[]
类型:[]int 、值:[]
类型:[]bool 、值:[false true]
类型:[]bool 、值:[false true]
*/
切片属性
切片有自己的长度、容量、分别通过len()、cap()求值。
- 切片的默认容量,是从切片的开始索引、到数组的最后
array > slice
package main
import "fmt"
func main() {
//玩法1:切片底层原理就是一个数组,所以切片可以基于数组,基于切片表达式,获得切片。
// 切片表达式 [头:尾] ,左闭 ,右开 ,得到的切片 长度 = 尾-头 ,容量cap同于数组的容量。
a1 := [5]int{1, 3, 5, 7, 9}
s1 := a1[1:3]
fmt.Printf("切片s1类型:%T、值:%v、长度:%d、容量:%d\n", s1, s1, len(s1), cap(s1))
//切片的默认容量,是从切片的开始索引、到数组的最后
// 切片s1类型:[]int、值:[3 5]、长度:2、容量:4
fmt.Printf("源数组a1类型:%T、值:%v、长度:%d、容量:%d\n", a1, a1, len(a1), cap(a1))
// 源数组a1类型:[5]int、值:[1 3 5 7 9]、长度:5、容量:5
//切片表达式,[low:high:max] max 理解为high可取的最大值,最终切片的容量等于 max - low
s2 := a1[1:3:5] // 头:尾:max
fmt.Printf("切片s2类型:%T、值:%v、长度:%d、容量:%d\n", s2, s2, len(s2), cap(s2))
// 切片表达式,简写
s3 := a1[:] // 复制,等于 a1[0:len(a1)]
s4 := a1[2:] // 等于a1[2:len(a1)]
s5 := a1[:3] // 等于a[0:3]
fmt.Printf("切片s3类型:%T、值:%v、长度:%d、容量:%d\n", s3, s3, len(s3), cap(s3))
fmt.Printf("切片s4类型:%T、值:%v、长度:%d、容量:%d\n", s4, s4, len(s4), cap(s4))
fmt.Printf("切片s5类型:%T、值:%v、长度:%d、容量:%d\n", s5, s5, len(s5), cap(s5))
}
/*
切片s1类型:[]int、值:[3 5]、长度:2、容量:4
源数组a1类型:[5]int、值:[1 3 5 7 9]、长度:5、容量:5
切片s2类型:[]int、值:[3 5]、长度:2、容量:4
切片s3类型:[]int、值:[1 3 5 7 9]、长度:5、容量:5
切片s4类型:[]int、值:[5 7 9]、长度:3、容量:3
切片s5类型:[]int、值:[1 3 5]、长度:3、容量:5
*/
直接定义slice
package main
import "fmt"
// 玩法3:指定索引
func slice1() {
s1 := []int{1, 2, 3}
fmt.Printf("切片s1类型:%T、值:%v、长度:%d、容量:%d\n", s1, s1, len(s1), cap(s1))
s2 := []int{5: 1} // 索引5的值为1 ,该slice,长度6,容量6
fmt.Printf("切片s2类型:%T、值:%v、长度:%d、容量:%d\n", s2, s2, len(s2), cap(s2))
/*
切片s1类型:[]int、值:[1 2 3]、长度:3、容量:3
切片s2类型:[]int、值:[0 0 0 0 0 1]、长度:6、容量:6
*/
}
func main() {
//玩法2:直接定义slice
//s1 := []int{1, 3, 5, 7, 9}
//fmt.Printf("切片s1类型:%T、值:%v、长度:%d、容量:%d\n", s1, s1, len(s1), cap(s1))
//
//s2 := s1[1:2:4] // 头:尾:4-1(cap)
//fmt.Printf("切片s2类型:%T、值:%v、长度:%d、容量:%d\n", s2, s2, len(s2), cap(s2))
//
slice1()
}
/*
切片s1类型:[]int、值:[1 3 5 7 9]、长度:5、容量:5
切片s2类型:[]int、值:[3]、长度:1、容量:3
*/
slice原理
slice是一个轻量级数据结构,提供访问数组子序列元素的功能。
slice由三个部分构成,指针、长度、容量
指针:指针指向slice
第一个元素对应的数组元素的地址。长度:slice元素的数量,不得超过容量。
容量:slice
开始的位置到底层数组的结尾。

package main
import "fmt"
func main() {
//创建数组,Months月份,1月份到12月份
months := [...]string{"", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
//创建切片,对数组的引用
s1 := months[4:7] //[April May June]
s2 := months[6:9] //[June July August]
fmt.Println(s1)
fmt.Println(s2)
//指针:指针指向slice`第一个元素`对应的`数组元素`的地址。
fmt.Printf("s1切片内存地址:%p\n", &s1[0])
fmt.Printf("s1切片对应数组元素的内存地址:%p\n", &months[4])
}
/*
[April May June]
[June July August]
s1切片内存地址:0xc0000a26c0
s1切片对应数组元素的内存地址:0xc0000a26c0
*/

slice修改
package main
import (
"fmt"
)
func main() {
array1 := [...]int{1, 3, 5, 7, 9} // 数组
s1 := array1[2:4] // 切片 [5,7]
fmt.Println("源数组:", array1)
fmt.Println("源切片:", s1)
//切片读写操作目标是底层数组
s1[0] += 100
s1[1] += 200
fmt.Println("修改后源数组:", array1)
fmt.Println("修改后切片:", s1)
}
make()构造切片

创建切片有2个方式
- 基于数组创建slice,数组可见
- 通过内置make()函数,创建slice,底层数组被隐藏
package main
import (
"fmt"
)
/*
内置make函数,参数(类型,len,cap),注意cap大于len,容量可以省略,默认等于长度
切片有默认值
如果你创建切片时,可以确定存储的元素个数,建议一次性给足cap,否则会产生扩容时的消耗
*/
var slice0 []int = make([]int, 10)
var slice1 = make([]int, 2)
var slice2 = make([]int, 3, 10)
func main() {
fmt.Printf("make全局slice0 :%v、长度:%d、容量:%d\n", slice0, len(slice0), cap(slice0))
fmt.Printf("make全局slice1 :%v、长度:%d、容量:%d\n", slice1, len(slice1), cap(slice1))
fmt.Printf("make全局slice2 :%v、长度:%d、容量:%d\n", slice2, len(slice2), cap(slice2))
fmt.Println("--------------------------------------")
slice3 := make([]int, 3)
slice4 := make([]int, 5)
slice5 := make([]int, 5, 7)
// 若是超出index范围、会panic报错
slice5[0] = 11
slice5[1] = 22
slice5[2] = 33
slice5[3] = 44
slice5[4] = 55
fmt.Printf("make局部slice3 :%v、长度:%d、容量:%d\n", slice3, len(slice3), cap(slice3))
fmt.Printf("make局部slice4 :%v、长度:%d、容量:%d\n", slice4, len(slice4), cap(slice4))
fmt.Printf("make局部slice5 :%v、长度:%d、容量:%d\n", slice5, len(slice5), cap(slice5))
}
/*
make全局slice0 :[0 0 0 0 0 0 0 0 0 0]、长度:10、容量:10
make全局slice1 :[0 0]、长度:2、容量:2
make全局slice2 :[0 0 0]、长度:3、容量:10
--------------------------------------
make局部slice3 :[0 0 0]、长度:3、容量:3
make局部slice4 :[0 0 0 0 0]、长度:5、容量:5
make局部slice5 :[11 22 33 44 55]、长度:5、容量:7
*/
直接定义切片
package main
import "fmt"
func main() {
//第三种方式,原理类似make,数组看不见,由make维护
var s1 []int = []int{1, 2, 3, 4, 5}
fmt.Println(s1)
fmt.Println(len(s1))
fmt.Println(cap(s1))
}
切片拷贝
若两个切片拷贝后,也共享同一个底层数组,修改切片会影响另一个切片。

package main
import "fmt"
func main() {
s1 := make([]int, 3) //[0 0 0]
s2 := s1 //将s1直接赋值给s2,s1和s2共用一个底层数组
s2[0] = 100
fmt.Printf("s1类型:%T、值:%v、长度:%d、容量:%d,底层数组内存地址:%p\n", s1, s1, len(s1), cap(s1), &s1[0])
fmt.Printf("s2类型:%T、值:%v、长度:%d、容量:%d,底层数组内存地址:%p\n", s2, s2, len(s2), cap(s2), &s2[0])
}
/*
s1类型:[]int、值:[100 0 0]、长度:3、容量:3,底层数组内存地址:0xc0000260a8
s2类型:[]int、值:[100 0 0]、长度:3、容量:3,底层数组内存地址:0xc0000260a8
*/
切片遍历
和数组一样,支持for range
package main
import "fmt"
func main() {
var arr [5]int = [...]int{11, 22, 33, 44, 55}
s1 := arr[1:4]
//for循环遍历
for i := 0; i < len(s1); i++ {
fmt.Printf("切片s1 i=%v 值=%v\n", i, s1[i])
}
fmt.Println()
//for range方式遍历切片
for i, v := range s1 {
fmt.Printf("切片s1 i=%v 值v=%v\n", i, v)
}
}
/*
切片s1 i=0 值=22
切片s1 i=1 值=33
切片s1 i=2 值=44
切片s1 i=0 值v=22
切片s1 i=1 值v=33
切片s1 i=2 值v=44
*/
切片练习
package main
import "fmt"
func main() {
var array1 = [...]int{11, 22, 33, 44}
slice1 := array1[1:3]
slice2 := array1[1:]
slice3 := array1[:]
slice4 := slice3[:2]
fmt.Println(slice1)
fmt.Println(slice2)
fmt.Println(slice3)
fmt.Println(slice4)
}
/*
[22 33]
[22 33 44]
[11 22 33 44]
[11 22]
*/
切片扩容append()函数
Go语言内置的append()函数,可以让切片动态添加新元素,一次添加一个、多个、甚至追加另一个切片。
每个切片都会指向一个底层数组,数组的容量还够就添加新元素,长度增大。
- 底层数组的容量不能容纳新元素时,切片按照一定规则扩容
- 此时切片指向的数组已更换
package main
import "fmt"
func main() {
//创建切片
var slice1 []int = []int{100, 200, 300}
fmt.Printf("slice1容量=%v 长度=%v\n", cap(slice1), len(slice1))
//给切片追加新元素,由于原本的len、cap都是3,再加薪元素,就触发了扩容
slice1 = append(slice1, 400)
fmt.Printf("新slice1值:%v、扩容后容量=%v 长度=%v\n", slice1, cap(slice1), len(slice1))
// 切片扩容切片
// slice1... 该语法糖代表展开切片元素
slice2 := []int{1, 2, 3, 4}
// 由于append扩容后的数组更换,我们一般会用源变量接收append返回值
slice1 = append(slice1, slice2...)
fmt.Printf("新slice1值:%v、扩容后容量=%v 长度=%v\n", slice1, cap(slice1), len(slice1))
}
/*
新slice1值:[100 200 300 400]、扩容后容量=6 长度=4
新slice1值:[100 200 300 400 1 2 3 4]、扩容后容量=12 长度=8
*/
查看slice扩容规则

package main
import "fmt"
func main() {
//append()添加元素和切片扩容
var numSlice []int
for i := 0; i < 100; i++ {
numSlice = append(numSlice, i)
fmt.Printf(" len:%d cap:%d 内存地址:%p\n", len(numSlice), cap(numSlice), numSlice)
}
}
支持追加多个元素
package main
import "fmt"
func main() {
//append()添加元素和切片扩容
var numSlice []int
fmt.Printf("源切片:%v、长度:%d、容量:%d、内存地址:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
numSlice = append(numSlice, 1, 2, 3, 4)
fmt.Printf("新切片:%v、长度:%d、容量:%d、内存地址:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
//若以如下语法糖追加新元素,不得传入多余参数
s2 := []int{77, 88}
numSlice = append(numSlice, s2...)
fmt.Printf("新切片:%v、长度:%d、容量:%d、内存地址:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
/*
源切片:[]、长度:0、容量:0、内存地址:0x0
新切片:[1 2 3 4]、长度:4、容量:4、内存地址:0xc0000220e0
新切片:[1 2 3 4 77 88]、长度:6、容量:8、内存地址:0xc0000240c0
*/
}
切片扩容原理

// 从小切片增长 2x 过渡
// 大切片增长 1.25 倍。 这个公式
// 在两者之间提供平滑的过渡。
测试
>>> 3408/2560
1.33125
>>> 2560/1792
1.4285714285714286
>>> 1792/1280
1.4
>>> 1280/848
1.509433962264151
>>> 848/512
1.65625
>>> 512/256
2.0
>>>
// 测试扩容机制
package main
import "fmt"
func main() {
//append()添加元素和切片扩容
var numSlice []int
for i := 0; i < 3000; i++ {
numSlice = append(numSlice, i)
fmt.Printf(" len:%d cap:%d 内存地址:%p\n", len(numSlice), cap(numSlice), numSlice)
}
}
切片拷贝copy()函数
Go语言提供了内置的copy()函数专门用于拷贝切片。
为何需要copy()
package main
import "fmt"
func main() {
//为什么需要copy()函数去完整拷贝新的切片
a1 := []int{1, 3, 5, 7, 9}
b1 := a1
fmt.Printf("类型:%T、值:%v,地址:%p\n", a1, a1, a1)
fmt.Printf("类型:%T、值:%v,地址:%p\n", b1, b1, b1)
//修改b1,同时也会修改a1
b1[0] = 111
fmt.Printf("类型:%T、值:%v,地址:%p\n", a1, a1, a1)
fmt.Printf("类型:%T、值:%v,地址:%p\n", b1, b1, b1)
}
/*
类型:[]int、值:[1 3 5 7 9],地址:0xc00001a180
类型:[]int、值:[1 3 5 7 9],地址:0xc00001a180
类型:[]int、值:[111 3 5 7 9],地址:0xc00001a180
类型:[]int、值:[111 3 5 7 9],地址:0xc00001a180
切片是引用类型,a、b都指向了同一个内存地址。
修改b导致了a也变化。
*/
使用copy
Go语言内置的copy()函数可以快速的将slice1的数据,复制到另一个新的slice2里。
//语法
copy(destSlice, srcSlice []T)
srcSlice: 数据来源切片
destSlice: 目标切片
案例
package main
import "fmt"
func main() {
// 两个的独立的切片,拷贝数据
a1 := []int{1, 3, 5, 7, 9}
b1 := make([]int, len(a1))
//完全拷贝
copy(a1, b1) //a1数据,拷给了b1
fmt.Printf("a1类型:%T、值:%v,地址:%p\n", a1, a1, a1)
fmt.Printf("b1类型:%T、值:%v,地址:%p\n", b1, b1, b1)
//修改b1,和a1无关了
b1[0] = 111
fmt.Printf("修改后a1类型:%T、值:%v,地址:%p\n", a1, a1, a1)
fmt.Printf("修改后b1类型:%T、值:%v,地址:%p\n", b1, b1, b1)
}
/*
a1类型:[]int、值:[0 0 0 0 0],地址:0xc00011a000
b1类型:[]int、值:[0 0 0 0 0],地址:0xc00011a030
修改后a1类型:[]int、值:[0 0 0 0 0],地址:0xc00011a000
修改后b1类型:[]int、值:[111 0 0 0 0],地址:0xc00011a030
*/
删除切片元素技巧
Go语言本身没提供删除切片元素的方法,可以利用切片表达式,删除部分元素、
package main
import "fmt"
func main() {
a1 := []int{1, 3, 5, 7, 9}
//利用append特性,截取丢弃数据
//删除元素5
// 语法,a = append(a1[:index],a1[index+1:]...)
a1 = append(a1[:2], a1[3:]...)
fmt.Println(a1)
}
string和slice的技巧
1.string底层就是byte数组,因此string可以有slice一样用法。
package main
import "fmt"
func main() {
str1 := "www.yuchaoit.cn"
//字符串可以切片
s1 := str1[:4]
fmt.Println(s1) // www.
}
2.如何修改字符串、转为切片后修改
package main
import "fmt"
func main() {
str1 := "www.yuchaoit.cn"
//类型强转 []byte(),这里是纯英文,若是汉字用[]rune处理
s1 := []byte(str1)
fmt.Printf("s1切片类型:%T、值:%v\n", s1, s1)
//修改切片元素,修改最后一个元素
s1[len(s1)-1] = 'c'
fmt.Printf("修改后s1切片类型:%T、值:%v\n", s1, s1)
//转回字符串
str1 = string(s1)
fmt.Printf("修改后str1字符串类型:%T、值:%v\n", str1, str1)
}
slice练习题
看题说答案
package main
import "fmt"
func main() {
var a = make([]string, 5, 10) //[] 5个空字符串
for i := 0; i < 10; i++ {
a = append(a, fmt.Sprintf("%v", i))
}
fmt.Printf("go原生值:%#v\n", a)
fmt.Printf("go普通值:%v\n", a)
}