基础类型2
数组
数组是Go语言中常见的数据结构之一。说下java中的数组吧,内存空间连续,支持随机查询,更新删除涉及到元素移动效率低。我之前写过一篇java数组和链表的区别,数组是指一系列同一类型的数据的集合,数组中间每个元素称为数组元素(element),一个数组包含的元素被称为数组的长度。数组的声明如下:
[32]byte //长度为32的数组,每个元素为一个字节
[2*n] struct{x,y int32} //复杂类型数组
[1000]*float64 //指针数组
[3][4]int //二维数组
[3][3][3]float64 // 等同于[3]([3]([3]float64))
数组可以是一维,和多维,比如[3]
和[4]
表示3行4列的二维整型数组,一共可以放12个元素。在Go语言中数组的长度定义后是不可更改的,在声明一个数组的长度可以是一个常量或者一个常量表达式(编译期间可以运算出结果的表达式)。数组的长度是数组类型的一个内置常量,可用Go语言中的内置函数len()获取,如下:
arrLength:=len(arr)
思考:数组我定义的长度为10如果我放进去11个元素,那我放第11个元素的时候是报错还是放不进去。
- 数组元素访问 可以使用数组下标来访问,与其他语言一样,数组的下标都是从0开始,最后一个元素的下标表示为len(arr)-1,如果想遍历数组并打印出值如下:
for i:=0;i<len(arr);i++ {
fmt.Printle("Element",i,"of array value is:",arr[i])
}
Go语言还提供了一个关键字range,用于便捷的遍历容器中的元素,当然range也是数组支持的范围,上文的遍历可以改为如下:
for i,v=range arr {
fmt.Printle(("Array element[", i, "]=", v)
}
在上面的例子里可以看到,range具有两个返回值,第一个返回值是元素的数组下标,第二个返回值是元素的值。
- 值类型 Go语言中数组是一个值类型(value type)。所有的值类型在赋值或者当参数传递的时候,都会产生一次复制操作,就是传入函数的数组原数组不变,会复制一份出来当值传递下去,如果在函数体内对该数组操作,是对你传入数组的复制的数组操作,而原数组不变,对应java里面的含义也是值传递。复制值传递,和值传递相反的java中是引用传递,就是传递的是引用指向的那块内存的值,如果你在传递函数中对值进行修改,将影响传递之前的值,因为没有复制,直接把我自己家的“钥匙”给你了,你去把我家东西搬动了,我自己家肯定就变变了,但是值传递是再复制一个一模一样的房子把钥匙给你。你再怎么折腾都影响不了我原来的房子。Go值类型举例:
package main
import "fmt"
func modify(array [10]int) {
array[0] = 10 // 试图修改数组的第一个元素
fmt.Println("In modify(), array values:", array)
}
func main() {
array := [5]int{1,2,3,4,5} // 定义并初始化一个数组
modify(array) // 传递给一个函数,并试图在函数体内修改这个数组内容
fmt.Println("In main(), array values:", array)
}
该程序的执行结果为:
In modify(), array values: [10 2 3 4 5]
In main(), array values: [1 2 3 4 5]
如代码。原数组1 2 3 4 5
不变,变的是复制的1 2 3 4 5
改变第一个元素变成10 2 3 4 5
。
- 数组切片 简单来说,数组是固定的大小,在实际开发中事先我不能确定数组的长度,那么Go语言解决这种场景出现了数组切片,能动态控制数组大小的容器,初看起来,数组切片就像一个指向数组的指针,实际上它拥有自己的数据结构,而不仅仅是个指针。数组切片的数据结构可以抽象为以下3个变量:
- 一个指向原生数组的指针。
- 数组切片中的元素个数。
- 数组切片已分配的存储空间。 创建数组切片的几种方式
基于数组:数组切片可以基于一个已存在的数组创建。数组切片可以只使用数组的一部分元素或者整个 数组来创建,甚至可以创建一个比所基于的数组还要大的数组切片
var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 基于数组创建一个数组切片
var mySlice []int = myArray[:5]
直接创建
- 创建一个初始元素个数为5的数组切片,元素初始值为0:
mySlice1 := make([]int, 5)
- 创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10)
- 直接创建并初始化包含5个元素的数组切片:
mySlice3 := []int{1, 2, 3, 4, 5}
注意:动态数组切片底层的动态其实是在长度不够的时候,重新分配一段“够大”的内存,把原来的元素复制一份去更大的内存空间,和java的数组扩容一样的道理。java动态扩容一般为增加为原来长度的一半,比如原数组有空间是10,增加一次新数组的空间大小为10+10/2=15,这种申请新空间再搬运元素的做法实现了动态,但是一次次的申请和搬运肯定影响效率,如果你事先知道大小设定为一个最大的大小空间长度,那么是最好不过了。
几个函数:
- cap()函数返回的是数组切片分配的空间大小
- len()函数返回的是数组切片中当前所存储的元素个数
- append()数组追加,比如在mySlice加上3个元素,从而生成一个新的数组切片:
mySlice = append(mySlice, 1, 2, 3)
- copy()数组复制,用于将内容从一个数组切片复制到另一个数组切片,如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制:
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
map
在Go中,使用map不需要引入任何库,map是一堆键值对的未排序集合
- 变量声明
var myMap map[string] PersonInfo
- 创建 我们可以使用Go语言内置的函数make()来创建一个新map
myMap = make(map[string] PersonInfo)
- 元素赋值 赋值过程非常简单明了,就是将键和值用下面的方式对应起来即可
myMap["1234"] = PersonInfo{"1", "雷小鸿", "Room 101,..."}
- 元素删除
Go语言提供了一个内置函数delete(),用于删除容器内的元素,举例删除key为
1234
的键值对,如果可以1234
不存在,将什么都不会发生,但是如果传入的map变量的值是nil,该调用将导致程序抛出异常(panic)。
delete(myMap, "1234")
- 元素查找
简单明了,举例从map取key为
1234
的value,并赋值给a
value, a := myMap["1234"]
if a { // 找到了
// 处理找到的value
}
判断是否成功找到特定的键,不需要检查取到的值是否为nil,只需查看第二个返回值ok, 这让表意清晰很多。配合:=操作符,让你的代码没有多余成分,看起来非常清晰易懂。
备注
本文正在参与「掘金Golang主题学习月」, 点击查看活动详情。