Golang:切片的声明初始化以及操作示例,详解概括

Posted 保暖大裤衩LeoLee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang:切片的声明初始化以及操作示例,详解概括相关的知识,希望对你有一定的参考价值。

Golang基础学习项目地址:https://github.com/LeoLeeWithWarmPants/golangStudyhttps://github.com/LeoLeeWithWarmPants/golangStudy

概述

slice本质上是一个数据结构(struct结构体)。

  • 切片是数组的一个引用,即切片的底层是一个数组,所以切片是一个引用类型。

  • 切片的长度是可以变化,由于其支持扩容(类似于Java的List),可以理解切片为一个“长度动态变化的数组”。

    • 需要注意的是,支持扩容并不代表可以越界操作

    • 扩容后,底层创建并引用了新的数组,将数据赋值到新的数组

  • 可以使用数组或者是make两种方式来初始化一个切片。

    • 需要注意的时make也是会创建底层数组的。数组的长度为make设置的capcity,数组元素的值为数组类型对应的默认值。

  • 切片可以被继续切片。产生的新切片和原切片公用同一个底层数组,修改新的切片会影响原切片

slice变量声明、初始化后,其内存空间内包含:

  • slice首个元素的地址(该元素对应原始数组的某个元素)

  • slice的长度

  • 容量(slice底层数组的长度)

在使用数组初始化切片的时候,一定要注意数组是值传递,还是作为了切片的底层数组通过make创建的切片顶层的数组只能通过切片来操作,该数组没有其他的引用

示例

package main

import (
	"errors"
	"fmt"
)

const length int = 5

var intArray = [length]int{1, 3, 5, 7, 9}

//从一个数组中获取一个切片
//通过make创建的切片顶层的数组只能通过切片来操作,该数组没有其他的引用
func getSliceFromArray(array [length]int) []int {
	slice := array[1:3] // 1:3 代表从数组下标为1的元素开始取值,到下标为3的元素位置,不包含3。如果从0开始引用数组,也可以省略冒号前的0(如 array[:3]),反之同理。
	return slice
}

//通过make创建一个切片
func getSliceByMake() []int {
	var slice3 []int = make([]int, 4, 8)
	fmt.Printf("slice3 length=%d, capcity=%d\\n", len(slice3), cap(slice3))
	return slice3
}

//创建切片时,初始化切片引用的数组
func getSliceByInitArray() []int {
	var slice4 []int = []int{1, 3, 5, 7, 9}
	fmt.Printf("slice4 length=%d, capcity=%d\\n", len(slice4), cap(slice4))
	return slice4
}

//对切片进行切片,从一个切片产生一个新的切片
func getSliceFromSlice(slice []int) []int {
	return slice[0:2]
}

//切片的遍历
func sliceTraverseByFori(slice []int) {
	fmt.Println("sliceTraverseByFori executing...")
	for i := 0; i < len(slice); i++ {
		if i == len(slice)-1 {
			fmt.Printf("slice[%d]:%d \\n", i, slice[i])
		} else {
			fmt.Printf("slice[%d]:%d ", i, slice[i])
		}
	}
	slice[0] = 2
}
func sliceTraverseByForRange(slice []int) {
	fmt.Println("sliceTraverseByForRange executing...")
	for idx, element := range slice {
		if idx == len(slice)-1 {
			fmt.Printf("slice[%d]:%d \\n", idx, element)
		} else {
			fmt.Printf("slice[%d]:%d ", idx, element)
		}
	}
}

//测试切片扩容
func testDilatationByAppend(slice1 []int, slice2 []int, args ...int) []int {
	if slice1 == nil {
		panic(errors.New("被扩容的切片不能为空"))
	}
	if slice2 != nil {
		slice1 = append(slice1, slice2...)
	}
	if args != nil && len(args) > 0 {
		slice1 = append(slice1, args...)
	}
	return slice1
}

func main() {
	//从一个数组中获取一个切片
	slice1 := getSliceFromArray(intArray)
	fmt.Printf("slice1 from an array, slice1=%v,length=%d,capacity=%d\\n", slice1, len(slice1), cap(slice1))
	fmt.Printf("intArray memory address:%p\\n", &intArray)
	fmt.Printf("intArray[1] memory address:%p\\n", &intArray[1])
	fmt.Printf("slice1 memory address:%p\\n", &slice1)
	fmt.Printf("slice1[0]=%d, slice1[0] memory address:%p\\n", slice1[0], &slice1[0]) // slice1[0] == intArray[1] == 3

	//由于getSliceFromArray函数中声明并初始化了slice1,intArray是基本类型,通过值传递进入了getSliceFromArray函数,所以修改slice1的值并不影响intArray
	slice1[0] = 2
	fmt.Printf("after slice1 changed,slice1=%v\\n", slice1)
	fmt.Printf("after slice1 changed,intArray=%v\\n", intArray)
	//当slice2和intArray2处在同一作用域时,slice2中的元素地址指向了intArray2中的元素,修改slice2的元素值等于修改intArray2中的元素
	intArray2 := [length]int{0, 2, 4, 6, 8}
	slice2 := intArray2[1:3]
	fmt.Printf("slice2=%v\\n", slice2)
	slice2[0] = 28
	fmt.Printf("after slice2 changed,slice2=%v\\n", slice2)
	fmt.Printf("after slice2 changed,intArray2=%v\\n", intArray2)

	//通过make创建切片
	slice3 := getSliceByMake()
	fmt.Printf("slice3:%v, slice3 memory address:%p\\n", slice3, &slice3)

	//创建切片时,初始化切片引用的数组
	slice4 := getSliceByInitArray()
	fmt.Printf("slice4=%v\\n", slice4)

	//切片fori遍历
	sliceTraverseByFori(slice4)
	sliceTraverseByFori(slice4) //由于切片是引用传递,所以方法内部对切片的变动,将会影响源数据
	sliceTraverseByForRange(slice4)

	//对切片进行切片,从一个切片产生一个新的切片
	fmt.Printf("slice4=%v\\n", slice4)
	slice5 := getSliceFromSlice(slice4)
	fmt.Printf("slice5=%v\\n", slice5)
	slice5[0] = 33
	fmt.Printf("after slice5 changed, slice4=%v\\n", slice4) //说明切片slice4产生的切片slice5,slice4和slice5公用一个底层的数组

	//切片扩容测试
	intArray3 := [...]int{1, 2}
	slice6 := intArray3[:]
	fmt.Printf("slice6=%v, length=%d, capcity=%d, address=%p\\n", slice6, len(slice6), cap(slice6), &slice6)
	slice7 := []int{3, 4, 5}
	slice6 = testDilatationByAppend(slice6, slice7)
	//由此可以看到切片在append元素之后,capcity增加了,虽然切片的地址没有变化,但是由数组声明后长度不可变可知,底层的数据肯定不是intArray3了
	fmt.Printf("slice6=%v, length=%d, capcity=%d, address=%p\\n", slice6, len(slice6), cap(slice6), &slice6)

	//切片的拷贝
	slice8 := []int{1, 2, 3}
	slice9 := make([]int, 6)
	copyLength := copy(slice9, slice8)
	fmt.Printf("copyLength=%d\\n", copyLength)
	fmt.Printf("slice8=%v\\n", slice8)
	fmt.Printf("slice9=%v\\n", slice9)
}

以上是关于Golang:切片的声明初始化以及操作示例,详解概括的主要内容,如果未能解决你的问题,请参考以下文章

Golang:切片的声明初始化以及操作示例,详解概括

golang切片详解

Golang Slice切片

golang基础 —— 切片几种声明方式

golang基础 —— 切片几种声明方式

Golang 基础:Go Module, for range, 切片, map, struct 等使用和实现