Go 语言入门指南:基础语法和常用特性解析 | 青训营

105 阅读8分钟

0、引言与概述

Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。

对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。

Go语言的特色可以粗略概括为以下三点:

  • 简洁、快速、安全
  • 并行、有趣、开源
  • 内存管理、数组安全、编译迅速

本文我将着重对Go语言很有特色的数组与切片slice、范围range、集合map进行介绍总结。

1、数组与切片

1.1、数组

1.1.1、简介

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。相对于去声明 number0, number1, ..., number99 的变量,使用数组形式 numbers[0], numbers[1] ..., numbers[99] 更加方便且易于扩展。

数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为0,第二个索引为1,以此类推。

1.1.2、声明数组的语法

       var arrayName [size]dataType

       其中,arrayName 是数组的名称,size 是数组的大小,dataType 是数组中元素的数据类型。

1.1.3、初始化数组

1.1.3.1、方法1

声明一个名为 numbers 的整数数组,其大小为 5,在声明时,数组中的每个元素都会根据其数据类型进行默认初始化,对于整数类型,初始值为 0。

var numbers [5]int

1.1.3.2、方法2、3

       var numbers = [5]int{1, 2, 3, 4, 5}

       numbers := [5]int{1, 2, 3, 4, 5}

1.1.3.3、注

       在 Go 语言中,数组的大小是类型的一部分,因此不同大小的数组是不兼容的,即[5]int和[10]int是不同的类型。

       如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

       如果设置了数组的长度,我们还可以通过指定下标来初始化元素:

// 将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}

初始化数组中{ }中的元素个数不能大于[ ]中的数字。

如果忽略[ ]中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小。

1.1.4、访问数组元素

1.1.5、二维数组、多维数组

1.2、切片

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型,切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

1.2.1、理解并辨析切片长度len和容量cap

       容量是指底层数组的大小,长度指可以使用的大小。

       容量的用处在于当append扩展长度时,如果新的长度小于容量,不会更换底层数组,否则,go 会新申请一个底层数组,拷贝这边的值过去,把原来的数组丢掉。也就是说,容量的用途是:在数据拷贝和内存申请的消耗与内存占用之间提供一个权衡。

       而长度,则是为了帮助你限制切片可用成员的数量,提供边界查询的。所以用make申请好空间后,需要注意不要越界(即越 len)

1.2.2、声明切片

       切片只有一种声明方式,与数组类似,但不指明大小:

       var sliceName []sliceType

1.2.3、切片的三种初始化方式

1.2.3.1、切片作为初始化值

var sliceName []sliceType = []sliceType{}

sliceName := []sliceType{}

切片定义是对数组的抽象,在例子中[]int{}可以简单看做是不用声明长度的数组。

数组和切片都有指向数组的指针。而切片比数组多了len和cap。len表示切片的长度,cap表示切片的容量。len与cap在未被定义时会被自动赋值:len为初始化值的长度0,cap为len。

1.2.3.2、数组作为初始化值

       array := […]int {0,1,2,3,4,5,6,7,8}        

var slice3 []int = array[0:9](sliceName := array[startIndex:endIndex])

       (1)这里是将数组下标 0 到 8 的元素放在切片中。

startIndex的缺省值为0,endIndex的缺省值为array的长度。  

(2)初始化后,切片的len为 endIndex - startIndex,cap为 array长度 - startIndex。

1.2.3.3、make函数返回值作为初始化值

       var sliceName []sliceType = make([]sliceType, length, capacity_optional)

       sliceName := make([]sliceType, length, capacity_optional)

1.2.4、切片声明时,不会在[]内规定大小,而数组必须规定。

1.2.5、切片常用函数

1.2.5.1、len() 和 cap() 函数

       切片是可索引的,并且可以由 len() 方法获取长度。

切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

1.2.5.2、append() 和 copy() 函数

       拷贝切片的 copy 方法和向切片追加新元素的 append 方法。

       如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。      

package main


import "fmt"


func main() {

   var numbers []int

   printSlice(numbers)



   /* 允许追加空切片 */

   numbers = append(numbers, 0)

   printSlice(numbers)


   /* 向切片添加一个元素 */

   numbers = append(numbers, 1)

   printSlice(numbers)


   /* 同时添加多个元素 */

   numbers = append(numbers, 2,3,4)

   printSlice(numbers)

   /* 创建切片 numbers1 是之前切片的两倍容量*/

   numbers1 := make([]int, len(numbers), (cap(numbers))*2)


   /* 拷贝 numbers 的内容到 numbers1 */

   copy(numbers1,numbers)

   printSlice(numbers1)  

}

func printSlice(x []int){

   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)

}

以上代码执行输出结果为:

len=0 cap=0 slice=[]

len=1 cap=1 slice=[0]

len=2 cap=2 slice=[0 1]

len=5 cap=6 slice=[0 1 2 3 4]

len=5 cap=12 slice=[0 1 2 3 4]

2、范围 range

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:

for key, value := range oldMap {
    newMap[key] = value
}

如果只想读取 key,格式如下:for key, _ := range oldMap 如果只想读取 value,格式如下:for _, value := range oldMap

3、集合 map

map 是一种无序的键值对的集合。

map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,map 是无序的,遍历 map 时返回的键值对的顺序是不确定的。

在获取 map 的值时,如果键不存在,返回该类型的零值,例如 int 类型的零值是 0,string 类型的零值是 ""。

map 是引用类型,如果将一个 map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。

3.1、map的定义

可以使用内建函数 make 或使用 map 关键字来定义 map:

/* 使用 make 函数 */
map_variable := make(map[KeyType]ValueType, initialCapacity)

其中 KeyType 是键的类型,ValueType 是值的类型,initialCapacity 是可选的参数,用于指定 Map 的初始容量。Map 的容量是指 Map 中可以保存的键值对的数量,当 Map 中的键值对数量达到容量时,Map 会自动扩容。如果不指定 initialCapacity,Go 语言会根据实际情况选择一个合适的值。

实例:

// 创建一个空的 Map
m := make(map[string]int)

// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)

也可以使用字面量创建 Map:m := map[string]int{ "apple": 1, "banana": 2, "orange": 3, }

获取元素:

v1 := m["apple"]
v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值

修改元素:m["apple"] = 5

获取 map 的长度:len := len(m)

遍历 map:

for k, v := range m {
    fmt.Printf("key=%s, value=%d\n", k, v)
}

删除元素:delete(m, "banana")

4、Go语言常用特性

Go语言的设计目标是简单、高效、可靠,并且易于编程,以解决其他编程语言的一些痛点和限制。

并发支持:Go 语言天生支持并发编程,提供了轻量级的Goroutines和通道(Channel)机制,使并发编程变得简单和高效。这使得编写并发程序变得容易,且能够充分利用多核处理器。

高性能:Go 语言在运行时性能方面表现优异。其编译器能够产生高效的机器码,使得 Go 程序在性能上能够与 C++ 等编程语言相媲美。

跨平台:Go语言支持跨多个平台,包括Windows、Linux、macOS等。开发者可以方便地编译和运行代码在不同的操作系统上。

强调工具链:Go语言提供了完善的工具链,包括代码格式化工具、测试工具、性能分析工具等,使得开发、测试和部署变得更加简单和高效。

丰富的标准库:Go语言内置了许多常用的库,涵盖网络、文件操作、加密、文本处理等多个领域,让开发者能够更轻松地构建复杂的应用程序。

总体而言,Go语言的背景和特色使其成为一门优秀的现代编程语言,适用于构建高性能、高并发的系统,也适合快速开发和轻松上手。它在云计算、网络服务、分布式系统等领域得到广泛应用,并持续在开发者社区中受到关注和支持。