从0开始go语言-6|Go主题月

469 阅读7分钟

基础类型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]

直接创建

  1. 创建一个初始元素个数为5的数组切片,元素初始值为0:mySlice1 := make([]int, 5)
  2. 创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:mySlice2 := make([]int, 5, 10)
  3. 直接创建并初始化包含5个元素的数组切片:mySlice3 := []int{1, 2, 3, 4, 5} 注意:动态数组切片底层的动态其实是在长度不够的时候,重新分配一段“够大”的内存,把原来的元素复制一份去更大的内存空间,和java的数组扩容一样的道理。java动态扩容一般为增加为原来长度的一半,比如原数组有空间是10,增加一次新数组的空间大小为10+10/2=15,这种申请新空间再搬运元素的做法实现了动态,但是一次次的申请和搬运肯定影响效率,如果你事先知道大小设定为一个最大的大小空间长度,那么是最好不过了。

几个函数:

  1. cap()函数返回的是数组切片分配的空间大小
  2. len()函数返回的是数组切片中当前所存储的元素个数
  3. append()数组追加,比如在mySlice加上3个元素,从而生成一个新的数组切片:mySlice = append(mySlice, 1, 2, 3)
  4. 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主题学习月」, 点击查看活动详情