go 语言进阶之数组切片
数组
数组是一组由固定长度的特定类型的元素组成的序列,一个数组可以由零个或多个元素组成。数组长度是数组类型的组成部分。因为数组的长度是数组类型的一部分,不同长度或不同类型的数据组成的数组都是不同的类型,因此go语言很少直接使用数组(不同长度的数组因为类型不同而无法直接赋值)。和数组对应的是切片,切片是可以动态增长收缩的序列,切片的功能更灵活,但理解切片的工作原理还是先理解数组;数组是值类型
示例
package main
import (
"fmt"
)
func main(){
var foarr [6]float64
foarr[0]= 3.0
foarr[1]= 5.0
foarr[2]= 1.0
foarr[3]= 3.4
foarr[4]= 2.0
foarr[5]= 50.0
var t float64
for i:=0;i<len(foarr);i++{
t = t+foarr[i]
}
//fmt.Sprintf("%.2f",t / 6) 将这个结果四舍五入保留到小数点两位返回值
a := fmt.Sprintf("%.2f",t / 6)
fmt.Println("平均体重",a)
}
// 执行结果
// 平均体重 10.73
为什么不能直接写成fmt.Sprintf("%.2f",t /len(foarr)) Go 语言 不允许不同类型直接计算。 写成6 Go 会自动推断因为 t 是 float64,所以 6 会被当成 float64 常量。
package main
import (
"fmt"
)
func main(){
var foarr [6]float64
foarr[0]= 3.0
foarr[1]= 5.0
foarr[2]= 1.0
foarr[3]= 3.4
foarr[4]= 2.0
foarr[5]= 50.0
var t float64
for i:=0;i<len(foarr);i++{
t = t+foarr[i]
}
//fmt.Sprintf("%.2f",t / 6) 将这个结果四舍五入保留到小数点两位返回值
a := fmt.Sprintf("%.2f",t / float64(len(foarr))) // 6 具体的数是常量可以类型推导;所以必须强制转换
fmt.Println("平均体重",a)
}
// 执行结果
// 平均体重 10.73
1) 使用数组解决问题,程序可维护性增加了
2)方法代码可维护性增加了,也容易扩展
数组的定义
var 数组名 [数组长度]数据类型
var a [5]int
赋初始值a[0]=1 a[1]=30 ....
数组内存图

示例
package main
import (
"fmt"
)
func main(){
var intArr [3]int// 定义好数组就有默认值各个数组
intArr[0]=6
intArr[1]=2
intArr[2]=3
fmt.Println(intArr)
fmt.Printf("intArr数组的地址值=%p,intArr数组的intArr[0] 地址=%p,intArr数组的intArr[1] 地址=%p\n",&intArr,&intArr[0],&intArr[1])
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo02\main.go
// [6 2 3]
// intArr数组的地址值=0xc000014198,intArr数组的intArr[0] 地址=0xc000014198,intArr数组的intArr[1] 地址=0xc0000141a0
内存分析

1)数组地址可以通过数组名字获取&intArr
2)数组的第一个元素的地址,是数组的首地址
3)数组的各个元素的地址间隔是依据数组的类型决定,比如int
初始化数组
package main
import (
"fmt"
)
func main(){
var intArr [3]int// 定义好数组就有默认值各个数组
intArr[0]=6
intArr[1]=2
intArr[2]=3
fmt.Println(intArr)
fmt.Printf("intArr数组的地址值=%p,intArr数组的intArr[0] 地址=%p,intArr数组的intArr[1] 地址=%p\n",&intArr,&intArr[0],&intArr[1])
for i,v:= range intArr{
fmt.Printf("数组下标=%v,数组下标对应的值=%v\n",i,v)// 遍历数组
}
var tr = [...]int {2:3,1:2}//定义一个长度为3的int类型数组,元素0,2,3 注:2:3 表示下标为2(下标从0开始)的元素值为3;1:2表示下标为1的元素值为2;0下标没有写默认还是初始值0
fmt.Println(tr)
var a = [...]int {2,6,8} //定义一个长度为3的int类型数组,元素2,6,8
var b = [...]int {1,2,4:6,8} //定义一个长度为6的int类型数组,元素1,2,0,0,6,8
fmt.Println(a)
fmt.Println(b)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo02\main.go
// [6 2 3]
// intArr数组的地址值=0xc000014198,intArr数组的intArr[0] 地址=0xc000014198,intArr数组的intArr[1] 地址=0xc0000141a0
// 数组下标=0,数组下标对应的值=6
// 数组下标=1,数组下标对应的值=2
// 数组下标=2,数组下标对应的值=3
// [0 2 3]
// [2 6 8]
// [1 2 0 0 6 8]
初始化示例2
package main
import (
"fmt"
)
func main(){
var c [3]int = [3]int {1,2,3}
fmt.Println(c)
var d = [3]int {1,2,4}
fmt.Println(d)
e := [...]int {4:5}
fmt.Println(e)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo02\main.go
// [1 2 3]
// [1 2 4]
// [0 0 0 0 5]
for-range 结构遍历
基本语法
for index,value := range array0{
}
说明
1)第一个返回值index是数组的下标
2)第二个返回值value 是数组下标位置的值
3)他们都是仅在for循环内部可见的变量
4)遍历数组元素的时候,如果不想使用下标或者值,可以使用下划线_
数组遍历
package main
import (
"fmt"
)
func main(){
var c [3]int = [3]int {1,2,3}
fmt.Println(c)
var d = [3]int {1,2,4}
fmt.Println(d)
e := [...]int {4:5}
fmt.Println(e)
for i,v := range e{
fmt.Printf("数组下标=%v,数组下标对应的值=%v\n",i,v)
}
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo02\main.go
// [1 2 3]
// [1 2 4]
// [0 0 0 0 5]
// 数组下标=0,数组下标对应的值=0
// 数组下标=1,数组下标对应的值=0
// 数组下标=2,数组下标对应的值=0
// 数组下标=3,数组下标对应的值=0
// 数组下标=4,数组下标对应的值=5
细节
1)数组是多个相同类型数据的组合,一个数组一旦声明/定义了其长度是固定的,不能动态变化
package main
import (
"fmt"
)
func main(){
var arr01 [3] int
arr01[0]=1
arr01[1]=30
// arr01[2]=1.1 cannot use 1.1 (untyped float constant) as int value in assignment (truncated)数据类型不同报错
arr01[2]=9
// arr01[3]=8 invalid argument: index 3 out of bounds [0:3] 数组下标超出范围报错;不能动态变化
fmt.Println(arr01)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo03\main.go
// [1 30 9]
2)var arr []int 这时arr 就是一个slice切片
3)数组中的元素可以是任何数据类型,包括值类型,引用数据类型
4)数组创建后,如果没有赋值,有默认值
数值型数组:默认值0
字符串数组: 默认值""
bool数组: 默认为false
package main
import (
"fmt"
)
func main(){
var arr02 [3] int
var arr03 [3] string
var arr04 [2] bool
fmt.Println(arr02)
fmt.Println(arr03)
fmt.Println(arr04)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo03\main.go
// [0 0 0]
// [ ]
// [false false]
5)使用数组的步骤1.声明数组并开辟空间2个给数组各个元素赋值3使用数组
6)数组下标从0开始的
7)数组下标必须在指定范围内使用,否则报panic:数组越界,比如 var arr [5] int 则有效下标为0-4
8)go的数组属值类型,在默认情况下是值传递,因此会进行值拷贝,数组之间不会影响
package main
import (
"fmt"
)
func test (arr [3]int){
arr[0] = 88
fmt.Println("test",arr)
}
func main(){
test(arr02)
fmt.Println("man",arr02)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo03\main.go
// test [88 0 0]
// man [0 0 0]
内存分析(值不同的内存分析)

9)如果想要在其他函数中,修改原来的数组,可以用引用值传递(指针方式)
package main
import (
"fmt"
)
func test1 (arr *[3]int){
(*arr)[0]=90 //(*arr[])
fmt.Println("test1",*arr)
}
func main(){
var arr02 [3] int
fmt.Println("man",arr02)
test1(&arr02)
fmt.Println("man",arr02)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo03\main.go
// man [0 0 0]
// test1 [90 0 0]
// man [90 0 0]
内存分析(值不同的内存分析)

10)长度是数组类型的一部分,在传递函数参数时需要考虑数组长度
示例
package main
import (
//"bytes"
"fmt"
)
func test (arr [3]int){
arr[0] = 88
fmt.Println("test",arr)
}
func test1 (arr *[3]int){
(*arr)[0]=90 //(*arr[])
fmt.Println("test1",*arr)
}
func main(){
var mychars [26]byte
for i,_ := range mychars{
mychars[i] = 'A' + byte(i)
}
for i,_:= range mychars{
fmt.Printf("%c ",mychars[i])
}
fmt.Println()
fmt.Println("------------")
//
var arr1 = [9]int {9,86,75,56,156,785,567,877,678}
var x int
var t int
for i,_ := range arr1{
if arr1[i] >= x {
x =arr1[i]
t = i
}
}
fmt.Printf("最大值=%v;最大值对应的下标是=%v\n",arr1[t],t)
//求平均值跟和
var arr2 [6]int = [6]int {56,78,98,45,56,89}
var s int
//var g int
for _,v := range arr2{
s += v
}
//如何让平均值保留小数
fmt.Printf("arr2 数组的和=%v,数组的平均值是=%v\n",s,float64(s)/float64(len(arr2)))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo04\main.go
// A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
// ------------
// 最大值=877;最大值对应的下标是=7
// arr2 数组的和=422,数组的平均值是=70.33333333333333
随机生成数组,并翻转数组
package main
import (
//"bytes"
"fmt"
"math/rand"
)
func test (arr [3]int){
arr[0] = 88
fmt.Println("test",arr)
}
func test1 (arr *[3]int){
(*arr)[0]=90 //(*arr[])
fmt.Println("test1",*arr)
}
func main(){
var t [6] int //初始化数组
for i,_ :=range t{
t[i]=rand.Intn(100)+1 //数组下标赋值
}
fmt.Println("交换前",t) //赋值后打印
y :=0
for i :=len(t)-1;i>=len(t)/2;i--{
t[i],t[y] = t[y],t[i] //交换两个值
y++
}
fmt.Println("交换后",t) //交换后打印
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo05\main.go
// 交换前 [18 85 29 93 13 5]
// 交换后 [5 13 93 29 85 18]
切片
切片就是一种简化版的动态数组。因为动态数组长度是不固定,切片的长度也就不能是类型的组成部分。虽然数组有适用的地方,但是数组类型和操作不够灵活,因此在go代码中数组使用并不多。切片则使用得相等广泛。理解切片原理
1)切片的英语单词slice
2) 切片是一个数组的引用,因此切片是引用数据类型,在进行传递时,遵守引用传递的机制
3)切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度len(slice)都一样
4)切片的长度是可以变化的,因此切片是一个动态数组
5)切片定义的基本语法
var 变量名 [] 类型
var(
a []int //nil切片,和nil相等,表示一个不存在的切片
b = []int {} // 空切片。和nil不相等,一般用来表示一个空集合
c = []int{1,2,3}//有3个元素的切片,len为2,cap为3
d = c[:2] //有两个元素的切片,len为2,cap为3
e = c[0:2:cap(c)] //有2个元素的切片。len为2,cap为3
f = c[:0] //有0个元素,len为0,cap为3
g = make([]int,3) //有3个元素的切片。len和cap都为3
h = make([]int,2,3) //有2个元素的切片。len为2,cap为3
i = make([]int,0,3)//有0个元素的切片。len为0,cap为3
)
示例
package main
import (
//"bytes"
"fmt"
_ "math/rand"
)
func test (arr [3]int){
arr[0] = 88
fmt.Println("test",arr)
}
func test1 (arr *[3]int){
(*arr)[0]=90 //(*arr[])
fmt.Println("test1",*arr)
}
func main(){
var in [5]int = [5] int {1,2,3,9,7}
s := in[1:3] //s:是切片的名字in[1:3]表示从下标为1的元素开始到下标为3的元素前一个,包左不包右
fmt.Println("数组",in)
fmt.Println("切片=",s)
fmt.Println("切片 长度=",len(s))
fmt.Println("切片 容量=",cap(s))//切片的容量是可以动态变化的;一般而言长度的两倍
fmt.Println("in len=",len(in))
fmt.Printf("in[1]的地址值%p\n",&in[1]) //打印数组下标为1的元素内存地址值
fmt.Printf("s[0]的地址值%p\n",&s[0]) //打印切片的内存地址值
fmt.Println("in 数组",in)
s[0]= 4 //修改切片第0个元素
fmt.Println("in 数组",in)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo06\main.go
// in [1 2 3 9 7]
// slice= [2 3]
// slice len= 2
// slice cap= 4
// in len= 5
// in[1]的地址值0xc00000e338
// s[0]的地址值0xc00000e338
// in 数组 [1 2 3 9 7]
// in 数组 [1 4 3 9 7]
内存分析

总结
1.slice 的确是一个引用类型
2slice 从底层来说,其实就是一个数据结构(struct 结构)
type slice struct {
data uintpte
len int
cap int
)
定义方式1: 定义一个切片,然后让切片去引用一个已经创建好的数组,例如前面的案例
定义方式2:var 切片名[] type = make([],len,[cap])
参数说明:type :就是数据类型;len:大小,cap:指定容量(可选)
func make(Type, size IntegerType) Type
内建函数make分配并初始化一个类型为切片、映射、或通道的对象。其第一个实参为类型,而非值。make的返回类型与其参数相同,而非指向它的指针。其具体结果取决于具体的类型:
切片:size指定了其长度。该切片的容量等于其长度。切片支持第二个整数实参可用来指定不同的容量;
它必须不小于其长度,因此 make([]int, 0, 10) 会分配一个长度为0,容量为10的切片。
映射:初始分配的创建取决于size,但产生的映射长度为0。size可以省略,这种情况下就会分配一个
小的起始大小。
通道:通道的缓存根据指定的缓存容量初始化。若 size为零或被省略,该信道即为无缓存的。
func cap(v Type) int
内建函数cap返回 v 的容量,这取决于具体类型:
数组:v中元素的数量,与 len(v) 相同
数组指针:*v中元素的数量,与len(v) 相同
切片:切片的容量(底层数组的长度);若 v为nil,cap(v) 即为零
信道:按照元素的单元,相应信道缓存的容量;若v为nil,cap(v)即
示例
package main
import (
"fmt"
)
func main(){
var(
// a []int //nil切片,和nil相等,表示一个不存在的切片
// b = []int {} // 空切片。和nil不相等,一般用来表示一个空集合
//c = []int{1,2,3}//有3个元素的切片,len为2,cap为3
// d = c[:2] //有两个元素的切片,len为3,cap为3;c开头到小标为2
// e = c[0:2:cap(c)] //有2个元素的切片。len为2,cap为3
// f = c[:0] //有0个元素,len为0,cap为3
g = make([]int,3) //有3个元素的切片。len和cap都为3;cap 省略,表示与
// h = make([]int,2,3) //有2个元素的切片。len为2,cap为3
// i = make([]int,0,3)//有0个元素的切片。len为0,cap为3
s []float64 =make([]float64, 4,10)
)
var k []int = make([]int, 4,8)
fmt.Printf("g的值:")
for _,v := range g{
fmt.Printf("%v ",v)
}
fmt.Println()
fmt.Println("g的容量:",cap(g))
fmt.Printf("s的值:")
for _,v:= range s{
fmt.Printf("%v ",v)
}
fmt.Println()
fmt.Println("s的容量:",cap(s))
fmt.Printf("k的值:")
for _,v:= range k{
fmt.Printf("%v ",v)
}
fmt.Println()
fmt.Println("k的容量:",cap(k))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// g的值:0 0 0
// g的容量: 3
// s的值:0 0 0 0
// s的容量: 10
// k的值:0 0 0 0
// k的容量: 8
make 内存分析图

1)通过make 方式创建切片可以指定切片的大小和容量
2)如果没有给切片的各个元素赋值,那么就会使默认值[int ,float=0 string="" bool=false ]
3)通过make 方式创建的切片对应的数组是由make底层维护,对外不可见,只能通过切片访问
方式三
package main
import (
//"bytes"
"fmt"
_ "math/rand"
)
func main(){
var j []int = []int {1,3,4}
fmt.Println("j的长度",len(j))
fmt.Println("j的容量",cap(j))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// j的长度 3
// j的容量 3
方式1与方式2的区别
方式1是直接引用数组,这个数组事先创建好的,程序员可见
方式2是通过make来创建切片,make会创建一个数组,是由切片在底层维护,程序员看不见。make创建切片的示意图

遍历切片
package main
import (
"fmt"
_ "math/rand"
)
func main(){
var j []int = []int {1,3,4}
fmt.Println("j的长度",len(j))
fmt.Println("j的容量",cap(j))
for i:=0;i<len(j);i++{
fmt.Printf("方式1:j的%v元素的值%v\n",i,j[i])
}
for i,v := range j{
fmt.Printf("方式2:j的%v元素的值%v\n",i,v)
}
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// j的长度 3
// j的容量 3
// 方式1:j的0元素的值1
// 方式1:j的1元素的值3
// 方式1:j的2元素的值4
// 方式2:j的0元素的值1
// 方式2:j的1元素的值3
// 方式2:j的2元素的值4
追加元素append
内置的泛型函数append 可以在切片的尾部追加N个元素;不过需要注意的是,在容量不足的情况下,append的操作会导致重新分配内存,可能导致巨大的内存分配和复制数据的代价。即使容量够,依然需要用append函数的返回值来更新切片本身,因为新的切片长度已经发生变化
package main
import (
//"bytes"
"fmt"
_ "math/rand"
_"net"
)
func main(){
var a []int //声明一个切片元素为空
fmt.Println(a)
a = append(a, 7) //追加1个元素
fmt.Println(a)
a = append(a, 2,7,8,9)//追加多个元素,手写解包方式
fmt.Println(a)
a = append(a, []int{1,2,3}...) //追加一个切片,切片需要解包
fmt.Println(a)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// []
// [7]
// [7 2 7 8 9]
// [7 2 7 8 9 1 2 3]
除了在切片尾部追加,还可以在切片的开头添加元素
package main
import (
//"bytes"
"fmt"
func main(){
var b []int = []int {1,2,3}
fmt.Println("b=",b)
b= append([]int{0},b...)//在开头添加一个元素
fmt.Println("b=",b)
b= append([]int{-3,-2,-1},b...) //在开头添加一个切片
fmt.Println("b=",b)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// b= [1 2 3]
// b= [0 1 2 3]
// b= [-3 -2 -1 0 1 2 3]
在开头一般都会导致内存的重新分配,而且会导致已有的元素全部赋值。因此,从切片的开头添加元素的性能一般比从尾部追加元素的性能差很多;
由于append函数返回新的切片,也就是它支持链式操作。可以将多个append操作组合起来,实现切片中间插入元素
package main
import (
"fmt"
}
func main(){
var c []int = []int {1,2,4}
fmt.Println("c=",c)
c = append(c[:1],append([]int{10},c[1:]...)...)//在下标1的位置插入10
fmt.Println("c=",c)
c = append(c[:1],append([]int{23,45,89},c[1:]...)...) //在下标1的位置插入切片
fmt.Println("c=",c)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// c= [1 2 4]
// c= [1 10 2 4]
// c= [1 23 45 89 10 2 4]
每个添加操作中的第二个append调用都会创键一个临时切片,并将c[1:]的内容复制到新创建的切片里,然后将临时创建的切片再追加到a[:i];可以用copy和append组合可以避免创建中间临时切片,同样完成元素添加;copy (c[3+1:],c[3:])//c[3:]向后移动一个位置 因为从第四个元素到后面所有元素拷贝。从第四(下标是3下标从0开始)个元素开始所有元素整体向后移,因为是拷贝下标是3的四个元素此时还是原来放入值;因此在下一步就是c[3]=8,就完成了添加元素
package main
import (
//"bytes"
"fmt"
)
func main(){
var c []int = []int {1,2,4}
fmt.Println("c=",c)
c = append(c[:1],append([]int{10},c[1:]...)...)//在下标1的位置插入10
fmt.Println("c=",c)
c = append(c[:1],append([]int{23,45,89},c[1:]...)...) //在下标1的位置插入切片
fmt.Println("c=",c)
c = append(c, 0) //切片扩展1个空间
copy (c[3+1:],c[3:])//c[3:]向后移动一个位置;
fmt.Println("copy操作后,c=",c)
c[3]=8// 重新设置下标为3的值
fmt.Println("copy操作并重新赋值后,c=",c)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// c= [1 2 4]
// c= [1 10 2 4]
// c= [1 23 45 89 10 2 4]
// copy操作后,c= [1 23 45 89 89 10 2 4]
// copy操作并重新赋值后,c= [1 23 45 8 89 10 2 4]
c = append(c, 0) //扩展切片的长度,为要插入的元素留出空间。第二句copy操作将要插入的位置开始之后的元素向后挪东一个位置。第三句真实地将新添加的元素赋值到对应的位置。操作语句虽然冗长一些。但相比前面方法可以减少临时切片
删除切片元素
根据要删除元素的位置有三种情况:从开头删除,从中间删除,从尾部删除,其中从尾部删除最快
package main
import (
"fmt"
)
func main(){
var a = [] int {1,2,3,4,5,6,7,8,9,10,12}
fmt.Println("开始删除前a",a)
a = a[:len(a)-1] //删除尾部一个元素
fmt.Println("删除尾部一个元素a",a)
a = a[:len(a)-3] //删除尾部3个元素
fmt.Println("删除尾部三个元素a",a)
a = a[1:] //删除开头1个元素
fmt.Println("删除开头1个元素a",a)
a = a[2:] //删除开头2个元素
fmt.Println("删除开头2个元素a",a)
a = append(a[:0],a[1:]...) //删除开头1个元素
fmt.Println("删除开头1个元素append*a",a)
a = append(a[:0],a[2:]...) //删除开头2个元素
fmt.Println("删除开头2个元素append*a",a)
var b = [] int {1,2,3,4,5,6,7,8,9,10,12,5}
fmt.Println("开始删除前b",b)
b = append(b[:2],b[2+1:]...) //删除中间一个元素
fmt.Println("删除中间一个元素b",b)
b = append(b[:2],b[2+2:]...) //删除中间两个元素
fmt.Println("删除中间二个元素b",b)
b = b[:1+copy(b[1:],b[1+1:])] //删除中间一个元素
fmt.Println("删除中间一个元素b(copy)",b)
b = b[:1+copy(b[1:],b[1+2:])] //删除中间两个元素
fmt.Println("删除中间两个元素b(copy)",b)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo08\main.go
// 开始删除前a [1 2 3 4 5 6 7 8 9 10 12]
// 删除尾部一个元素a [1 2 3 4 5 6 7 8 9 10]
// 删除尾部三个元素a [1 2 3 4 5 6 7]
// 删除开头1个元素a [2 3 4 5 6 7]
// 删除开头2个元素a [4 5 6 7]
// 删除开头1个元素append*a [5 6 7]
// 删除开头2个元素append*a [7]
// 开始删除前b [1 2 3 4 5 6 7 8 9 10 12 5]
// 删除中间一个元素b [1 2 4 5 6 7 8 9 10 12 5]
// 删除中间二个元素b [1 2 6 7 8 9 10 12 5]
// 删除中间一个元素b(copy) [1 6 7 8 9 10 12 5]
// 删除中间两个元素b(copy) [1 8 9 10 12 5]
细节
切片初始化时 var slice = arr[startIndex:end:index];说明 从arr数组下标为startindex,取到下标为endindex的元素(不含arr[endindex])
切片初始化时,仍然不能越界。范围在[0-len(arr)]之间,但是可以动态增长
1)var slice = arr[0:end] 可以简写 var slice = arr [:end]
2) var slice = arr[start:len(arr)] 可以简写 var slice= arr[statrt:]
3) var slice = arr[0:len(arr)] 可以简写:var slice= arr[:]
cap 是一个内置函数,用于统计切片的容量,即量大可以存放多个元素
切片定后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用
切片可以继续切片
示例
package main
import (
//"bytes"
"fmt"
_ "math/rand"
_"net"
)
func main(){
var c []int = []int {1,2,4}
fmt.Println("c=",c)
c = append(c[:1],append([]int{10},c[1:]...)...)//在下标1的位置插入10
fmt.Println("c=",c)
c = append(c[:1],append([]int{23,45,89},c[1:]...)...) //在下标1的位置插入切片
fmt.Println("c=",c)
c = append(c, 0) //切片扩展1个空间
copy (c[3+1:],c[3:])//c[3:]向后移动一个位置;
fmt.Println("copy操作后,c=",c)
c[3]=8// 重新设置下标为3的值
fmt.Println("copy操作并重新赋值后,c=",c)
d := c[1:4]
fmt.Println(d)
fmt.Println(&c[1])
fmt.Println(&d[0])
fmt.Println(cap(d))
// d = append([]int{0},d...)
d =append(d, 1,2,3)
fmt.Println(&c[1])
fmt.Println(&d[0])
var t [5]int = [5]int {3,7,8,8,5}
e := t[:5]
fmt.Println("数组1",&t[0])
fmt.Println("e1",&e[0])
e = append(e,1,2,3 )
fmt.Println("数组",&t[0])
fmt.Println("e",&e[0])
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// c= [1 2 4]
// c= [1 10 2 4]
// c= [1 23 45 89 10 2 4]
// copy操作后,c= [1 23 45 89 89 10 2 4]
// copy操作并重新赋值后,c= [1 23 45 8 89 10 2 4]
// [23 45 8]
// 0xc00006a0c8
// 0xc00006a0c8
// 11
// 0xc00006a0c8
// 0xc00006a0c8
// 数组1 0xc00000e420
// e1 0xc00000e420
// 数组 0xc00000e420
// e 0xc0000102d0
用append 内置函数,可以对切片进行动态加入
切片append 操作的底层原理分析
1)切片append 操作的本质就是对数组扩容
2)go底层会创建一下新的数组Newarr(扩容后的大小)
3)将slice原因元素拷贝新数组
4)slice 重新引用Newarr
5)注意Newarr 是底层来维护的
示例
package main
import (
//"bytes"
"fmt"
_ "math/rand"
_"net"
)
func test (arr [3]int){
arr[0] = 88
fmt.Println("test",arr)
}
func test1 (arr *[3]int){
(*arr)[0]=90 //(*arr[])
fmt.Println("test1",*arr)
}
func main(){
var g []int = []int {1,2,3}
fmt.Println(g)
g = append(g, 4)
fmt.Println(g)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// [1 2 3]
// [1 2 3 4]
内存分析

拷贝操作
切片的使用copy内置函数完成拷贝
说明copy(para1,para2):para1和para2都是切片
示例
package main
import (
//"bytes"
"fmt"
_ "math/rand"
_"net"
)
func test (arr [3]int){
arr[0] = 88
fmt.Println("test",arr)
}
func test1 (arr *[3]int){
(*arr)[0]=90 //(*arr[])
fmt.Println("test1",*arr)
}
func main(){
var g []int = []int {1,2,3}
fmt.Println(g)
g = append(g, 4)
fmt.Println(g)
var b []int = []int {1,2,3,4,5}
var c = make([]int,10)
fmt.Println(c)
copy(c,b)
fmt.Println(c)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo07\main.go
// [1 2 3]
// [1 2 3 4]
// [0 0 0 0 0 0 0 0 0 0]
// [1 2 3 4 5 0 0 0 0 0]
1)copy 参数必须是切片
2) 按照拷贝一个切片给另一个切片 修改一个切片的值不会影响另一个切片
string 和slice
1)string 底层是一个byte 数组,因此string也可以进行切片处理
示例
import (
"fmt"
//"slices"
)
func main(){
str := "[email protected]"
slice := str[6:]
for _,v := range slice{
fmt.Printf("%q",v)
}
fmt.Println("\n",slice)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo09\main.go
// 'c''h''e''n''x''i''.''c''o''m'
// chenxi.com
2)string 和切片在内存上的形式,以“abcd” 画出内存示意图
3)string 是不可变的,也就说不能通过str[0]=‘z’方式修改字符串
4)如果需要修改字符串,可以将string-> [] byte /或者[]rune -> 修改- >重写转成string
package main
import (
"fmt"
//"slices"
)
func main(){
str := "[email protected]"
slice := str[6:]
for _,v := range slice{
fmt.Printf("%q",v)
}
fmt.Println("\n",slice)
//修改string
//slice2 := str[:]
fmt.Println("打印初始数组",str)
arr := []byte(str) //转为切片
arr[0] = 'z' //修改下标为0 的元素
str = string(arr) //重新转成string 并赋值给原来变量
fmt.Println("打印修改后数组",str)
//细节,我们转成byte后,可以处理英文和数字,无法处理中文;原因是byte是按字节编码的
//解决方法:[]rune 是按字符处理
arr1 := []rune(str)
arr1[0]= '陈'
str = string(arr1)
fmt.Println("rune 修改汉字",str)
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo09\main.go
// 'c''h''e''n''x''i''.''c''o''m'
// chenxi.com
// 打印初始数组 [email protected]
// 打印修改后数组 [email protected]
// 陈[email protected]
示例
package main
import (
"fmt"
//"slices"
)
func fbn (n int) ([]uint64){
fbnSlice := make([]uint64,n)
for i,_ := range fbnSlice{
if i == 0 || i==1{
fbnSlice[i]=1
}else {
fbnSlice[i]= fbnSlice[i-1] +fbnSlice[i-2]
}
}
return fbnSlice
}
func main(){
fmt.Println("值=",fbn(30))
}
// 执行结果
// PS D:\golang\goproject\src\src01\go_code\src> go run chapter06\demo10\main.go
// 值= [1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040]

浙公网安备 33010602011771号