Go基础-数组和切片

1 定义

数组存储一系列相同类型的值,定义数组的语法是:

关键字 变量名 数组长度 数组元素类型
var a [3]int 

定义一个数组,初值为[0,0,0,…]

package main

import "fmt"

func main() {
	var a [3]int
	fmt.Println(a) // [0 0 0]
}

获取数组元素并给数组元素赋值

  • 设置元素语法

    package main
    
    import "fmt"
    
    func main() {
    	var a [3]int
    	a[0] = 12  // 给第一个元素赋值
    	a[1] = 13  // 给第二个元素赋值
    	a[2] = 14  // 给第三个元素赋值
    	fmt.Println(a) // [12 13 14]
    }
    
    
  • 读取元素语法

    package main
    
    import "fmt"
    
    func main() {
    	var a [3]int
    	a[0] = 12
    	a[1] = 13
    	a[2] = 14
    	fmt.Println(a) // [12 13 14]
    	for i := 0; i < len(a); i++ {
    		fmt.Printf(" %d", a[i])  // 12 13 14
    	}
    }
    
    

2 通过简略声明创建数组

语法

变量名 := [元素个数]元素类型{元素1,元素2,元素3}
a := [3]int{12,13,14}

测试

package main

import "fmt"

func main() {
	a := [5]int{1, 2, 3, 4, 5}
	for i := 0; i < len(a); i++ {
		fmt.Printf(" %d", a[i]) // 1 2 3 4 5
	}
}

3 通过简略声明创建数组

创建数组的时候可以用…代替,编译器会帮助计算数组的元素个数。

语法

// 变量名 := [...]元素类型{元素1,元素2,元素3,...}
a := [...]int{1,2,3,4,5,6}

测试

package main

import "fmt"

func main() {
	a := [...]int{1, 2, 3, 4, 5, 6}
	for i := 0; i < len(a); i++ {
		fmt.Printf(" %d", a[i])  //  1 2 3 4 5 6
	}
}

4 可以给数组赋一个值

对于一个有三个元素的数组,可以给数组赋一个值,没必要三个值都赋。对于其他没有赋值的元素,Go会默认将其赋值为零值。

package main

import "fmt"

func main() {
	a := [3]int{12}
	fmt.Println(a) // [12 0 0]
}

5 数组的长度是数组类型的一部分

数组的长度是类型的一部分,所有[3]int 和 [5]int不是相同类型。如果把一个长度为3的整数数组赋值给长度为5的整数数组,会直接报错的。

测试数组长度也是数组的一部分

package main

import "fmt"

func main() {
	a := [3]int{1, 2, 3}
	var b [5]int
	b = a  // cannot use a (type [3]int) as type [5]int in assignment
	fmt.Println(b)
}

6 数组是值类型不是引用类型

数组是一个值类型,意味着当把一个数组变量赋值给另一个变量时,是拷贝了一份副本,而不是拷贝了地址(引用类型是通过地址访问),所以当对原数组进行修改的时候,是不会影响到其他副本的,因为在内存中是有两个不同的区域分别存储,所以互不影响。

package main

import "fmt"

func main() {
	nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
	newnbastars := nbastars // 复制一个副本给newnbastars
	nbastars[0] = "jordan"  // 对原数组修改,不会影响到副本
	fmt.Println(nbastars)
	fmt.Println(newnbastars)
}

/*
[jordan james wade paul curry]
[kobe james wade paul curry]

*/

数组做为函数参数,也是传值

package main

import "fmt"

func changeArr(a [5]string) {
	a[0] = "new value"
	fmt.Println("in changeArr:", a)
}

func main() {
	nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
	fmt.Println("调用函数前:", nbastars)
	changeArr(nbastars)
	fmt.Println("调用函数后:", nbastars)
}

// 数组通过值传递,所以不会改变原数组的数据。

//调用函数前: [kobe james wade paul curry]
//in changeArr: [new value james wade paul curry]
//调用函数后: [kobe james wade paul curry]

7 数组长度

通过len(arr) 返回arr数组的长度,len函数通常与数组遍历配合使用,数组的索引范围为:0~len(arr)-1。

通过for+len循环遍历数组

package main

import "fmt"

func main() {
	nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
	for i := 0; i < len(nbastars); i++ {
		fmt.Printf("【%d】---【%s】\n", i, nbastars[i])
	}
	fmt.Println()
}


返回值

0---【kobe】
【1---【james】
【2---【wade】
【3---【paul】
【4---【curry】

8 通过range对数组进行遍历

package main

import "fmt"

func main() {
	nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
	for index, value := range nbastars {
		fmt.Printf("【%d】---【%s】\n", index, value)
	}
}

可以获取与for相同的输出。

如果只想获取元素,对索引不敏感,可以使用“_”下划线替换掉

package main

import "fmt"

func main() {
	nbastars := [...]string{"kobe", "james", "wade", "paul", "curry"}
	for _, value := range nbastars {
		fmt.Printf("【any】---【%s】\n", value)
	}
}

输出结果

【any】---【kobe】
【any】---【james】
【any】---【wade】 
【any】---【paul】 
【any】---【curry】

9 多维数组

Go支持多维数组,多维数组同样具有两种创建和初始化的方式,第一种是简单声明,第二种是先声明,然后通过索引逐个初始化,同样的,二维数组同样需要对数组的长度做限制。

通过正常方式创建二维数组

package main

import "fmt"

func printArr(a [4][13]int) {
	for _, v1 := range a {
		for _, v2 := range v1 {
			fmt.Printf("%d ", v2)
		}
		fmt.Println()
	}
}

func main() {
	var poke [4][13]int
	poke[0][0] = 01
	poke[0][1] = 02
	poke[1][0] = 14
	poke[2][0] = 27
	poke[3][0] = 40
	printArr(poke)
	fmt.Println(poke)
}

简明声明

package main

import "fmt"

func printArr(a [2][3]string) {
	for _, v1 := range a {
		for _, v2 := range v1 {
			fmt.Printf("%s ", v2)
		}
		fmt.Println()
	}
}


func main() {
	a := [...][3]string{  // 第一个维度可以用...表示,第二个维度就不行了
		{"物理", "化学", "生物"},
		{"历史", "政治", "地理"},
	}
	printArr(a)
	fmt.Println(a)
}

这里面的第一个维度可以不写长度,第二个维度必须写长度,其实这个和C语言的二维数组一样,第一个维度是可以不需要写长度的,第二个维度必须写长度,因为第二个维度是决定二维数组的内存跨度的。

10 切片的定义

切片是由数组建立的一种方便、灵活且功能强大的包装(Wrapper)。切片本身不拥有任何数据。它们只是对现有数组的引用。

10.1 创建一个切片

[]T表示T类型的切片

从数组创建一个切片

package main

import "fmt"

func main() {
	a := [...]int{1, 2, 3, 4, 5}
	var b []int = a[1:3]  // create slice from array
	fmt.Printf("type of b=%T, value of b=%s", b, b) // type of b=[]int, value of b=[%!s(int=2) %!s(int=3)]
}

arr[start: end] 获取arr数组的start索引到end-1索引。

{1,2,3,4,5} [1:3] 所以获取到的索引是 1 :2 返回的值是[2, 3]

10.2 使用简单方法创建一个切片

package main

import "fmt"

func main() {
	a := []int{1, 2, 3, 4, 5} // 创建一个数组,并且返回他的引用,这个引用就是slice类型
	fmt.Println(a) // [1,2,3,4,5]
}

11 修改切片

切片本身不拥有数据,所有的数据都存储在底层的数组中,切片是数组的一种表示,修改切片,会影响底层数组中的数据的变化。

package main

import "fmt"

func main() {
	a := []int{1, 2, 3, 4, 5} // 创建一个数组,并且返回他的引用,这个引用就是slice类型
	b := a
	fmt.Println("修改b前:", a)
	for i, _ := range (b) {
		b[i] += 2
	}
	fmt.Println("修改b后:", a)
}



输出结果

修改b前: [1 2 3 4 5]
修改b后: [3 4 5 6 7]

显然与数组的结果是不一样的,数组是值类型,切片是引用类型。所有切片的副本对切片的操作最终都将反馈到底层数组中。

12 将切片做为函数的参数

切片做为函数的参数,传递到函数内部的其实也是数组的引用,如果在函数内部修改切片的值,同样会直接影响底层数组中的值。

package main

import "fmt"

func changeSlice(a []int) {
	for i, _ := range a {
		a[i] += 1
	}
}

func main() {
	a := []int{1, 2, 3, 4, 5, 6, 7, 8}
	fmt.Println("调用函数之前:", a)
	changeSlice(a)
	fmt.Println("调用函数之后:", a)
	
}

函数返回结果

调用函数之前: [1 2 3 4 5 6 7 8]
调用函数之后: [2 3 4 5 6 7 8 9]

从运行结果也能看出来,切片是传递的引用,对切片的修改会直接影响底层数组。

13 切片的长度和容量

切片的长度指的是切片中包含元素的个数

切片的容量指的是切片所引用的底层数组的长度

容量反应了切片最多的元素能有多少个

package main

import "fmt"

func main() {
	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
	var b []int
	b = a[0:5]
	fmt.Printf("slice b len:%d, slice b capatity:%d", len(b), cap(b))
	// slice b len:5, slice b capatity:8
}

在上面的实例中可以发现,切片的长度是5个,容量就是底层数组的长度,len(a) = 8

14 使用make创建切片

func make([]T,len,cap)[]T 通过传递类型,长度和容量来创建切片。容量是可选参数, 默认值为切片长度。make 函数创建一个数组,并返回引用该数组的切片

package main

import "fmt"

func main() {
	i := make([]int, 5, 8)
	fmt.Println(i) //[0 0 0 0 0]
	// 重置切片
	i = i[:cap(i)]
	fmt.Println(i) //[0 0 0 0 0 0 0 0]
}

15 切片追加元素

有一个问题可能会困扰你。如果切片由数组支持,并且数组本身的长度是固定的,那么切片如何具有动态长度。以及内部发生了什么,当新的元素被添加到切片时,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回这个新数组的新切片引用。现在新切片的容量是旧切片的两倍

package main

import "fmt"

func main() {
	players := []string{"kobe", "james", "jordan"}
	fmt.Printf("size of players:%d,cap of players:%d\n", len(players), cap(players))
	players = append(players, "paul")
	fmt.Printf("size of players:%d,cap of players:%d\n", len(players), cap(players))
	players = append(players, "yao", "curry", "topson")
	fmt.Printf("size of players:%d,cap of players:%d\n", len(players), cap(players))
}

返回结果

size of players:3cap of players:3  
size of players:4cap of players:6
size of players:7cap of players:12

从返回结果看,和理论是相符的。

16 多维切片

多维切片与多维数组类似,切片也可以有多个维度。

17 内存优化

如果一个切片引用一个数组,当程序对这个切片进行操作的时候,底层的数组将不会被垃圾回收机制回收,如果有一个比较大的数组,切片只活跃在很小的范围内,这就导致了大量的内存浪费。我们通过一个实例来看怎么做内存优化。
func copy(dst,src[]T)int 来生成一个切片的副本。这样我们可以使用新的切片,原始数组可以被垃圾回收。

package main

import "fmt"

func main() {
	citys := [...]string{"北京", "上海", "重庆", "成都", "广州", "深圳", "东京", "南京"}
	cityslice := citys[0:3]
	var cityslicenew []string
	r := copy(cityslicenew, cityslice[0:cap(cityslice)-2])
	fmt.Println(r)
}

cityslicenew 是cityslice 的一个副本,这样copy之后citys就会被垃圾回收了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kobe_OKOK_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值