GoLang(6)数组与切片

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tangyaya8/article/details/86768175

计算机能存储的数据结构

我们学过数据结构这门课程的同学都知道,在我们常用的编程中有很多数据结构,比如最基本的线性数据结构:数组,链表。非线性数据结构:树,图,等等。但是这些很多的数据结构在计算机中只有两种存储,一种是基于连续内存存储的数组,另外一种是基于非连续存储的链表,有了这些基本的存储结构,我们才能衍生出各种适合存储,计算,等等适合特定场景的数据结构。今天我们来学习golang语言中的连续内存存储结构-----数组和其衍生物(切片)

数组

数组这种数据结构在我们编程中非常常见,因为其良好的随机读取性能和简单的结构,能满足很多读取特定某个元素的场景,很受编程界欢迎
数组是在连续存储空间开辟空间,一旦开辟特定空间则不能修改

新建数组

用var关键字来定义数组并初始化

func createArray(){
	//1:定义一个长度为10的int类型的数组
	var arr = [10]int
	//给数组赋值,数组的索引是从0开始到n-1
	arr[0] = 1
	arr[1] = 2
	//如果不给数组赋值,则默认是该数组类型的‘0’值
	//2:在定义数组的时候,给数组初始化
	//或者开辟一个长度为10的int类型的数组,但是前6个值是给定的,其余的值为'0'值
	var arr2 =[10]int{1,2,3,4,5,6}
}

用类型推导来定义并初始化数组,其实和变量中的类型推导差不多,使用:=来类型推导做了两步:1.定义一个类型为value的变量,2.初始化改变量(赋值)

func createArray(){
	//使用类型推导来初始化一个数组自定义长度的数组
	arr := [10]int{1,2,3,4}
	arr := [5]int{}
	//注意,使用类型推导创建数组的时候必须初始化数组,原因在上面介绍了
}

使用数组

上面我们介绍了两种创建数组的方式,我们来介绍下怎么使用数组

func useArray(){
	//创建一个数组
	arr := [10]int{1,2,3,4}
	//访问数组的第一个数据
	fmt.Println(arr[0])
	//访问数组中的元素使用数组名称[数组下标索引]
}

切片(slice)

我们知道数组的特性是一旦定义或初始化长度不能改变,在很多场景都不能满足我们的需要,很多编程语言都对数组这种数据结构进行了封装,比如java中的ArrayList,是可边长,自动扩容,缩容的,为我们的使用带来了很大的方便,在GoLang语言中我们也有类似的操作----切片

切片的底层还是数组,是对底层数组的一层封装,确切的说,切片是底层数组的一个view

创建切片

切片是动态数组,和创建数组的语法类似,只是在创建的时候无须给切片指定特定的长度

//使用var关键字创建切片
var slice = []int{1,2,3}
//使用类型推导来创建切片
slice := []int{1,2,3,4}

将数组转换为切片

var arr = [10]string{"hello","world"}
//将全部元素转化为切片
slice := arr[:]
//将部分元素转换为切片
slice := arr[0:3]
//注意切片的左右区间包含左区间的元素,不包含右区间的元素

使用make函数创建切片

slice := make([]int, 5)
make函数包含两个参数,第一个是创建的类型,第二个是初始化的长度

切片的长度和容量

创建的切片有两个属性,分别是长度(length),容量(capacity)

长度是指:该切片具有的元素数量,比如var arr= []string{"re","go","ms","app"}
这个切片的len为4
容量:容量是指切片的底层数组所具有的长度:
var arr = [10]int{1,2,3}
对这个数组进行切片
slice = arr[:]
则该切片的容量为10

切片的扩容机制

分以下几种情况:
1.切片每次新增个数不超过原来的1倍,且每次增加数不超过1024个,且增加后总长度小于1024个,这种情况下扩容后为原来的2倍

	    s1 := make([]int, 0)
	    fmt.Printf("The capacity of s1: %d\n", cap(s1))
	    for i := 1; i <= 17; i++ {
	        s1 = append(s1, i)
	        fmt.Printf("s1(%d): len: %d, cap: %d\n", i, len(s1), cap(s1))
	    }
	    fmt.Println()

结果:
/*
The capacity of s1: 0
s1(1): len: 1, cap: 1
s1(2): len: 2, cap: 2
s1(3): len: 3, cap: 4
s1(4): len: 4, cap: 4
s1(5): len: 5, cap: 8
s1(6): len: 6, cap: 8
s1(7): len: 7, cap: 8
s1(8): len: 8, cap: 8
s1(9): len: 9, cap: 16
s1(10): len: 10, cap: 16
s1(11): len: 11, cap: 16
s1(12): len: 12, cap: 16
s1(13): len: 13, cap: 16
s1(14): len: 14, cap: 16
s1(15): len: 15, cap: 16
s1(16): len: 16, cap: 16
s1(17): len: 17, cap: 32
*/
2.切片一次新增个数超过原来1倍,但不超过1024个,且增加后总长度小于1024个,这种情况下扩容后比实际具有的总长度还要大一些。

    s2 := make([]int, 10)
    fmt.Printf("The capacity of s2: %d\n", cap(s2))
    r1 := append(s2, make([]int, 5)...)
    fmt.Printf("r1: len: %d, cap: %d\n", len(r1), cap(r1))
    r2 := append(s2, make([]int, 11)...)
    fmt.Printf("r2: len: %d, cap: %d\n", len(r2), cap(r2))
    r3 := append(s2, make([]int, 21)...)
    fmt.Printf("r3: len: %d, cap: %d\n", len(r3), cap(r3))
    fmt.Printf("注意:像r2,r3 一次增加个数超过原容量的1倍,增加后结果比实际总长度预想的稍大一点 \n")
    fmt.Println()

结果:
/*
The capacity of s2: 10
r1: len: 15, cap: 20
r2: len: 21, cap: 22
r3: len: 31, cap: 32
注意:像r2,r3 一次增加个数超过原容量的1倍,增加后结果比实际总长度预想的稍大一点
*/
3.原切片长度超过1024时,一次增加容量不是2倍而是0.25倍,每次超过预定的都是0.25累乘

    // 1024 * 0.25 = 256
    s3 := make([]int, 1024)
    fmt.Printf("The capacity of s3: %d\n", cap(s3))
    r4 := append(s3, make([]int, 200)...)
    fmt.Printf("r4: len: %d, cap: %d\n", len(r4), cap(r4))
    r5 := append(s3, make([]int, 256)...)
    fmt.Printf("r5: len: %d, cap: %d\n", len(r5), cap(r5))
    // 1280 * 0.25 = 320
    r6 := append(s3, make([]int, 266)...)
    fmt.Printf("r6: len: %d, cap: %d\n", len(r6), cap(r6))
    fmt.Printf("注意:像r6 一次增加个数超过原容量的0.25倍,增加后结果比原容量1.25倍预想的稍大一点 \n")
    fmt.Println()
    

结果:
/*
The capacity of s3: 1024
r4: len: 1224, cap: 1280
r5: len: 1280, cap: 1280
r6: len: 1290, cap: 1696
注意:像r6 一次增加个数超过原容量的0.25倍,增加后结果比原容量1.25倍预想的稍大一点
*/
说明下:第三种情况,又分为两种,一种是原长度不小于1024时,一次扩容后为原来的1.25倍(当然一次增加的个数不超过原长度的0.25倍);第二种情况就是一次增加个数超过原长度的0.25倍,扩容后其容量比预想的总长度稍大点,就比如 r6: len: 1290, cap: 1696, 一次扩容长度大于0.25倍,扩容后,其实际容量比0.25累乘预想的大一点,比如10241.251.25 = 1600 ,实际为1696.

向切片追加元素

我们说过切片是动态的数组,自然可以动态的增加元素,切片使用appen方法可以在切片的末尾追加元素

slice := make([]int,5)
append(slice,3)
append(slice,4)
append(slice,5)
如果原来的切片容量不足,则会扩容更大的一个切片,切片的扩容请看上文

切片的循环

在go语言中提供了两种方式用来循环数组或者切片
**方式一:**普通的for循环

func loopSlice() {
	var slice = []int{1, 2, 3, 4, 5,}
	for i:= 0; i < len(slice); i++ {
		fmt.Println(slice[i])
	}
}

**方式二:**使用range方式来迭代,注意此方式会返回两个值,一个为数组下表,一个为下标所对应的值

func rangeSlice() {
	var slice = []int{1, 2, 3, 4, 5,}
	for _, v := range slice {
		fmt.Println(v)
	}
}

猜你喜欢

转载自blog.csdn.net/tangyaya8/article/details/86768175