Go 语言 切片的使用

Go 语言 切片的使用

一、切片的基本介绍:

  1. 切片(slice)数组的一个引用,因此切片是引用类型。在传递时,遵守引用传递机制。

  2. 切片的使用和数组相似:

    比如:遍历切片、访问切片的元素、求切片的长度等等;

  3. 切片是一个可以动态变化的数组

  4. 切片定义的基本语法:

    var 切片名 []类型

    比如:var sliceName []int

    定义切片名为sliceName 类型为int64

切片基本使用

package main

import "fmt"

func main() {
	// 切片的基本使用
	intArr := [5]int{1, 2, 3, 4, 5}
	// slice = intArr[1:3]
	// 1.slice 就是切片名
	// 2.intArr[1:3], 表示 slice 引用到了intArr这个数组
	// 3.=引用intArr数组的起始下标为1, 结束下标为3(不包含结束下标) 左闭右开
	slice := intArr[1:3]
	fmt.Println("数组intArr元素为:", intArr)
	fmt.Println("切片元素为:", slice)
	fmt.Println("切片的长度为:", len(slice))
	fmt.Println("切片的容量为:", cap(slice))  // 切片的容量是可以动态变化的

	// intArr[1] 的地址 跟 slice[0]的地址相同
	fmt.Printf("intArr[1]的地址:%p\n", &intArr[1])
	fmt.Printf("slice[0]的地址:%p", &slice[0])
}
// 输出结果:
数组intArr元素为: [1 2 3 4 5]
切片元素为: [2 3]
切片的长度为: 2
切片的容量为: 4
intArr[1]的地址:0xc00000c308
slice[0]的地址:0xc00000c308

二、切片在内存中的布局:

以上代码,如图所示
在这里插入图片描述

总结

  1. slice 是一个引用类型;

  2. slice 就是一个数据结构**(struct结构体)**;

    type slice struct {

    ​ ptr *[2]int // ptr 数据类型 数组

    ​ len int // len 数据类型 int

    ​ cap int

    }

修改slice值,会影响到数组,因为slice是引用类型

package main

import "fmt"

func main() {
	// 切片的基本使用
	intArr := [5]int{1, 2, 3, 4, 5}
	slice := intArr[1:3]
	slice[0] = 100
	fmt.Println("intArr的数据为:", intArr)
}
// 输出结果:
intArr的数据为: [1 100 3 4 5]

三、切片的三种使用方式:

  1. 定义一个切片,然后让切片去引用一个已经创建好的数组

    package main
    
    import "fmt"
    
    func main() {
    	// 切片的第一种使用方式, 引用数组
    	var arr [5]int = [...]int{1, 2, 3, 4, 5}
    	var slice = arr[1:3]
    	fmt.Println("arr:", arr)
    	fmt.Println("slice:", slice)
    	fmt.Println("slice的长度:", len(slice))
    	fmt.Println("slice的容量:", cap(slice))
    }
    // 输出结果:
    arr: [1 2 3 4 5]
    slice: [2 3]
    slice的长度: 2
    slice的容量: 4
    
  2. 通过make来创建切片:

    基本语法

    var 切片名[]type = make([]type, len, cap)

    参数说明:

    1. type:数据类型;

    2. len:指定大小;

    3. cap:指定切片容量

      若分配cap,需要 cap >= len

    举个栗子

    package main
    
    import "fmt"
    
    func main() {
    	// 切片定义的第二种方式, 通过make 来进行创建
    	var slice []float64 = make([]float64, 5, 10)
    	slice[0] = 1
    	slice[3] = 2
    
    	fmt.Println("slice的值:", slice)
    	fmt.Println("slice的长度:", len(slice))
    	fmt.Println("slice的容量:", cap(slice))
    }
    // 输出结果:
    slice的值: [1 0 0 2 0]
    slice的长度: 5
    slice的容量: 10
    

    以上代码如图所示
    在这里插入图片描述

    方式二总结

    1. 通过make 创建的切片,可以指定大小、容量;

    2. 若没有给切片赋值,则使用默认值

      int float 类型为: 0

      string 类型为:""

      bool 类型为:false

    3. 通过make方式创建切片对应的数组是由make底层维护,对外不可见,只能通过slice去访问各个元素;

  3. 定义一个切片,直接指定具体数组,原理类似make:

    package main
    
    import "fmt"
    
    func main() {
    	// 直接定义一个切片
    	var strSlice []string = []string{"python", "golang", "java"}
    	fmt.Println("strSlice的值:", strSlice)
    	fmt.Println("长度:", len(strSlice))
    	fmt.Println("容量:", cap(strSlice))
    }
    // 输出结果:
    strSlice的值: [python golang java]
    长度: 3
    容量: 3
    

1与2的区别

方式一:是直接引用数组,这个数组事先存在的,我们是可见的;

方式二:是通过make创建切片,make也会创建数组,是由切片在底层进行维护的;

四、切片遍历:

  1. for 循环常规遍历:

    package main
    
    import "fmt"
    
    func main() {
    	// for 循环常规遍历:
    	var arr = [...]int{10, 20 ,30 ,40, 50}
    	slice := arr[1:4]
    	for i := 0; i < len(slice); i++ {
    		fmt.Printf("slice[%v]=%v\n", i, slice[i])
    	}
    }
    // slice[0]=20
    slice[1]=30
    slice[2]=40
    
  2. for-range 循环遍历:

    package main
    
    import "fmt"
    
    func main() {
    	// for-range 循环遍历
    	mySlice := []int{10, 20, 30, 40, 50}
    	for index, value := range mySlice{
    		fmt.Printf("index=%v;value=%v\n", index, value)
    	}
    }
    // 输出结果:
    index=0;value=10
    index=1;value=20
    index=2;value=30
    index=3;value=40
    index=4;value=50
    

五、切片注意事项与使用细节:

  1. 切片初始化时需注意:

    var slice = arr[startIndex: endIndex]

    说明:arr数组下标为startIndex,取到下标为endIndex的元素(不含endIndex)

    左闭右开

  2. 切片初始化时,不可越界可以动态增长

    var slice = arr[0:end] // 可以简写为: var slice = arr[:end]

    var slice = arr[start:len(arr)] // 可以简写为: var slice = arr[start:]

    var slice = arr[0:len(arr)] // 可以简写为: var slice = arr[:]

  3. cap是一个内置函数,用于统计切片容量,最大可存放多少个元素:

  4. 切片定义完后,不能使用,因为本身是空的,需要引用到一个数组,或者make一个空间给切片使用;

  5. 切片可继续切片:

    package main
    
    import "fmt"
    
    func main() {
    	slice01 := []int{10, 20, 30, 40, 50}
    	slice02 := slice01[1:4]  // 20, 30, 40
    	fmt.Println("slice02的元素:", slice02)
    	slice03 := slice02[:2] // 20, 30
    	fmt.Println("slice03的元素:", slice03)
    
    	// slice01,slice02,slice03 指向的数据空间是一个
    	slice03[0] = 666
    	fmt.Println("修改后的slice03:", slice03)
    	fmt.Println("查看之前slice01:", slice01)
    }
    // 输出结果:
    slice02的元素: [20 30 40]
    slice03的元素: [20 30]
    修改后的slice03: [666 30]
    查看之前slice01: [10 666 30 40 50]
    

六、内置函数append使用:

用append内置函数,可以对切片进行动态追加

package main

import "fmt"

func main() {
	// 用append 内置函数, 可以对切片进行动态追加
	var slice []int = []int{10, 20, 30}
	// 通过append 直接给slice 追加元素
	newSlice := append(slice, 40, 50, 60)
	fmt.Println("newSlice的值:", newSlice)

	// 通过append 将切片slice 追加到 slice里面
	slice = append(slice, slice...)
	fmt.Println("slice的新值:", slice)
}
// 输出结果:
newSlice的值: [10 20 30 40 50 60]
slice的新值: [10 20 30 10 20 30]
  1. 切片append操作本质,就是对数组扩容;
  2. go 底层会创建一个新的数组;
  3. 新的数组是在底层维护的;

七、内置函数copy的使用:

切片使用copy内置函数完成拷贝:

package main

import "fmt"

func main() {
	// 切片copy拷贝操作
	slice := []int{1, 2, 3, 4, 5}
	slice01 := make([]int, 10)
	copy(slice01, slice)
	fmt.Println("slice=", slice)  // 1, 2, 3, 4, 5
	fmt.Println("slice01=", slice01) // 1, 2, 3, 4, 5, 0, 0, 0, 0 ,0
}
// 输出结果:
slice= [1 2 3 4 5]
slice01= [1 2 3 4 5 0 0 0 0 0]
  1. copy(params01, params02),参数的数据类型是切片

  2. slice 与 slice01 的数据空间是独立,相互不影响;

    // 切片copy拷贝操作
    slice := []int{1, 2, 3, 4, 5}
    slice01 := make([]int, 10)
    copy(slice01, slice)
    fmt.Println("slice=", slice)  // 1, 2, 3, 4, 5
    slice01[1] = 100
    fmt.Println("slice01=", slice01) // 1, 100, 3, 4, 5, 0, 0, 0, 0 ,0
    

    slice01 与 slice 是两个独立的空间, slice01的修改, 并不会影响到slice

八、string和slice 之间的注意事项:

  1. string底层是一个byte数组,因此string 也可以进行切片操作:

    package main
    
    import "fmt"
    
    func main() {
    	// string 底层是一个byte数组, 因此string 也可以进行切片操作
    	myString := "python@java"
    	// 将java取出
    	strJava := myString[7:]
    	fmt.Println("strJava结果为:", strJava)
    }
    // 输出结果:
    strJava结果为: java
    
  2. string是不可变的

    不能通过myString[0] = “go”

  3. 若要修改字符串,可通过将string转换成[]byte,或者[]rune转换成string

    string 转换成[]byte(英文或数字即可)

    package main
    
    import "fmt"
    
    func main() {
    	// string 底层是一个byte数组, 因此string 也可以进行切片操作
    	myString := "python@java"
    
    	// 将"python@java"改成golang@java
    	byteArr := []byte(myString)
    	byteArr[0] = 'g'
    	byteArr[1] = 'o'
    	byteArr[2] = 'l'
    	byteArr[3] = 'a'
    	byteArr[4] = 'n'
    	byteArr[5] = 'g'
    	myString = string(byteArr)
    	fmt.Println("myString:", myString)
    }
    // 输出结果:
    myString: golang@java
    

    []rune转换成string(中文转换)

    package main
    
    import "fmt"
    
    func main() {
    	// []rune 转换成string
    	myString := "您好北京!"
    	runeString := []rune(myString)
    	runeString[2] = '上'
    	runeString[3] = '海'
    	myString = string(runeString)
    	fmt.Println("myString:", myString)
    
    }
    // 输出结果:
    myString: 您好上海!
    

    细节

    1. 转换成[]byte后,可以处理英文和数字,但是不能处理中文,原因是[]byte 是通过字节处理,而一个汉字,是3个字节,因此会出现乱码;
    2. 将 string 转换成[]rune即可,因为[]rune是按字符处理,兼容汉字;
发布了158 篇原创文章 · 获赞 172 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/Fe_cow/article/details/104144014