golang 切片基础用法


切片

  • 切片是围绕动态数据来构建的
  • 数组一旦创建就不能更改长度,但是切片可以按需求自动增长和缩小
  • 增长是使用内置的append函数来实现的
  • 缩小通过对切片的再次切片来实现

切片内部实现

切片分为 data len cap

直接声明和初始化

package  main

import "fmt"

func	 main(){
	var s1 []int			//声明切片
	fmt.Println(s1 )
	

	s1 =append(s1,1)		//切片追加 相当于增长切片
	s1 =append(s1,2)
	fmt.Println(s1)

	var s2 = []int{1,2,3}  //声明并初始化
	fmt.Println(s2)

}


-------------------   //输出结果
[]
[1 2]
[1 2 3]

使用make初始化

  • make([]int,长度,容量)
  • 以类型0值+长度的个数填充slice
  • 容量不填默认=长度,如果填了不能小于长度,可大于长度
package main

import "fmt"

func main() {

	// 使用make初始化一个长度为0的slice
	s1 := make([]int, 0)     // 不写容量默认跟长度一致
	s1 = append(s1, 1)
	s1 = append(s1, 2)
	s1 = append(s1, 3)
	fmt.Println(s1)
    fmt.Println(cap(s1))   //4

	// 使用make 初始化一个长度为5 容量为5的slice
	s2 := make([]int, 5, 5) 
	s2 = append(s2, 1)
	s2 = append(s2, 2)
	s2 = append(s2, 3)
	fmt.Println(s2) // [0 0 0 0 0 1 2 3]
}

new和make对比

  • 简单说 new只分配内存,make用于slice,map,和channel的初始化。
  • 对比表格
函数名 适用范围 返回值 填充值
new new可以对所有类型进行内存分配 new 返回指针 new 填充零值
make make 只能创建类型(slice map channel) make 返回引用 make 填充非零值

通过切片创建新的切片

  • 语法如下
slice[start:end:cap]
  • 其中 start 表示从 slice 的第几个元素开始切
  • end 控制切片的长度(end-start)
  • cap 控制切片的容量 ,如果没有给定 cap ,slice的长度值,则表示切到底层数组的最尾部
  • 新切片的长度 = end-start
  • 新切片的容量 = cap-start
  • cap不能大于原切片的cap
package main

import "fmt"

func main() {

	s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Printf("[s1][值:%v][新切片的长度=%d 容量=%d]\n", s1, len(s1), cap(s1))
	s2 := s1[2:6]
	fmt.Printf("[s2][从索引为2 就是3号往后切4个元素][值:%v][新切片的长度=%d 容量=%d]\n", s2, len(s2), cap(s2))
	s3 := s1[5:]
	fmt.Printf("[s3][从索引为5 就是6号 切到最后][值:%v][新切片的长度=%d 容量=%d]\n", s3, len(s3), cap(s3))
	s4 := s1[:4]
	fmt.Printf("[s4][从开头切4个元素][值:%v][新切片的长度=%d 容量=%d]\n", s4, len(s4), cap(s4))
	s5 := s1[:]
	fmt.Printf("[s5][复制整个切片][值:%v][新切片的长度=%d 容量=%d]\n", s5, len(s5), cap(s5))

	s6 := s1[2:6:8]
	fmt.Printf("[s6][从索引为2 就是3号往后切4个元素][值:%v][新切片的长度=%d 容量=%d]\n", s6, len(s6), cap(s6))

	s7 := s1[2:6:9]
	fmt.Printf("[s7][从索引为2 就是3号往后切4个元素][值:%v][新切片的长度=%d 容量=%d]\n", s7, len(s7), cap(s7))

}

改变某一个切片的元素

  • 说明所有切片的值都变了
fmt.Printf("[改变某一个切片的元素。看看其他切片会受影响吗]\n")
s6[1] = 80
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
fmt.Println(s5)
fmt.Println(s6)
fmt.Println(s7)

image

切片是引用类型,浅拷贝

package main

import "fmt"

func main() {
   a1 := []int{1, 2, 3}
   a2 := a1
   a2[0] = 10
   fmt.Println(a1, a2)
   a1[2] = 30
   fmt.Println(a1, a2)
}

使用copy函数进行深copy

  • 它表示把切片 src 中的元素拷贝到切片 dst 中
  • 返回值为拷贝成功的元素个数
  • 如果 src 比 dst 长,就截断
  • 如果 src 比 dst 短,则只拷贝 src 那部分:
package main

import "fmt"

func main() {
	a1 := []int{1, 2, 3}
	a2 := make([]int, 3)
	a3 := make([]int, 5)
	copy(a2, a1)
	copy(a3, a1)

	a2[0] = 10
	fmt.Println(a1, a2)
	a1[2] = 30
	fmt.Println(a1, a2)
	fmt.Println(a3)
}

切片作为参数传给函数

  • 虽然函数传参是指传递,应该是深拷贝
  • 但是slice属于引用类型,浅拷贝,在函数内部的修改还是会影响外部
package main

import "fmt"

func showSlice(s []int) {
	fmt.Printf("[传入的切片为:%v]\n", s)
	s[2] = 30
}

func main() {
	a1 := []int{1, 2, 3}
	showSlice(a1)
	fmt.Printf("[showSlice处理后的切片为:%v]\n", a1)
}

切片遍历

for range 遍历

  • 返回索引和值
  • range 创建了每个元素的副本,而不是直接返回对该元素的引用
package main

import "fmt"

func main() {
	a1 := []int{10, 20, 30}
	// 遍历查看值
	for index, num := range a1 {
		fmt.Printf("[index :%d,num:%d]\n", index, num)
	}
	//遍历修改值
	for index := range a1 {
		a1[index] += 100
	}
	for index, num := range a1 {
		fmt.Printf("[index :%d,num:%d]\n", index, num)
	}
}

切片扩容和cap字段作用

  • 假设没有cap只有len,怎么扩容
    1. slice长度为10,len=10,元素已经满了,现在要插入第11个元素
    2. slice长度扩为20,len=20,此时有用的元素为11个,还有9个空位。
    3. slice对外界暴露的接口只有ptr和len=20,如果此时再需要插入元素
    4. slice到底应该扩容还是在原有的基础上直接插入呢,如果直接插入从哪个索引插入
  • cap字段的作用就是为了方便扩容
package main

import "fmt"

func main() {

	a1 := []int{10, 11, 12, 13}
	fmt.Printf("len:%d cap:%d\n", len(a1), cap(a1))
	a1 = append(a1, 22)
	fmt.Printf("len:%d cap:%d\n", len(a1), cap(a1))
}
  • 当这个 append 操作完成后,newSlice 拥有一个全新的底层数组,这个数组的容量是原来的两倍
  • append() 会智能地处理底层数组的容量增长。在切片的容量小于 1000 个元素时,总是会成倍地增加容量。一旦元素个数超过 1000,容量的增长因子会设为 1.25,也就是会每次增加 25%的容量(随着语言的演化,这种增长算法可能会有所改变)。
package main

import "fmt"

func main() {

	a1 := []int{10, 11, 12, 13}
	fmt.Printf("len:%d cap:%d\n", len(a1), cap(a1))
	a1 = append(a1, 22)
	fmt.Printf("len:%d cap:%d\n", len(a1), cap(a1))
	a2 := make([]int, 1000)
	a1 = append(a1, a2...)
	fmt.Printf("len:%d cap:%d\n", len(a1), cap(a1))
	a3 := make([]int, 1000)
	a1 = append(a1, a3...)
	fmt.Printf("len:%d cap:%d\n", len(a1), cap(a1))

}

Author: qianli
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source qianli !
  TOC