Go 快速开发 | 05 - Go 中的切片和字典

1,327 阅读7分钟

一、切片 Slice

切片定义

Go 中的切片是一种数据类型,与 Python 中的切片是不同的概念,Go 中的切片类似于 Java 中的 ArrayList,支持自动扩容,是同一类型的集合并且要求连续的内存空间。

切片是引用数据类型,是基于数组的封装,内部结构包含了地址、长度和容量。

// 切片的声明,names 切片变量名,T 表示切片中存储的数据类型
var names []T
func main() {

   // 切片类型的定义

   var names []string
   fmt.Println(names)
   fmt.Println(names == nil)

   var ages = []int{}
   fmt.Println(ages)
   fmt.Println(ages == nil)

   var isBools = []bool{false, true}
   fmt.Println(isBools)
   fmt.Println(isBools == nil)

}

执行上述代码,输出结果如下:

[]
true
[]
false
[false true]
false

切片只能与 nil 进行比较,nil 在 Go 中类似其他语言中的 NULL,这里的 nil 是指内存地址为空,要注意的是长度和容量为 0 的 切片不一定是 nil。比如一个数组的元素被清空后,仍有内存地址,所以不为 nil。

切片是对数组的封装,包含了底层数组的指针(指向的内存地址)、容量和长度;容量是指切片中可以保存的元素数量,长度是指实际存储的元素的数量,所有 长度 <= 容量的

image.png

切片的长度可以通过 len() 函数获取,容量可以通过 cap() 函数获取

func main() {

   var emperors = []string{"朱棣", "朱高炽", "朱瞻基"}

   fmt.Println("emperors 切片的长度为:", len(emperors))
   fmt.Println("emperors 切片的容量为:", cap(emperors))

   // 删除索引为1的元素,生成新的切片
   newEmperors := append(emperors[:1], emperors[2:]...)
   fmt.Println(newEmperors)
   fmt.Println("newEmperors 切片的长度为:", len(newEmperors))
   fmt.Println("newEmperors 切片的容量为:", cap(newEmperors))

}

执行上述代码,输出结果如下:

emperors 切片的长度为: 3
emperors 切片的容量为: 3
[朱棣 朱瞻基]
newEmperors 切片的长度为: 2
newEmperors 切片的容量为: 3

根据输出的结果可以确定,切片的长度始终是小于等于切片的容量的这里使用到了 append 方法对切片中的指定的数据进行删除,会在切片的操作一节详述。

切片遍历

切片的遍历字符串的遍历基本一致,也有两种遍历方式

func main() {

   var cities = []string{"顺天", "应天", "奉天"}

   // 第一种遍历方式
   for i := 0; i < len(cities); i++ {
      fmt.Println(cities[i])
   }

   // 第二种遍历方式
   for _, city := range cities {
      fmt.Println(city)
   }

}

执行上述代码,输出结果如下:

顺天
应天
奉天
顺天
应天
奉天

切片操作

追加单个元素

Go 语言中切片的内建函数 append() 可以为切片动态添加数据,每个切片的底层都是一个数组,当底层数组的没有足够的容量存放新添加的元素时,切片会按照一定的规则进行扩容,这个扩容是会开辟一块新的内存空间。

func main() {

   var cities = []string{"顺天", "应天", "奉天"}
   fmt.Println("追加前长度为:", len(cities), ",容量为:", cap(cities))

   // 追加一个
   cities = append(cities, "承天")

   fmt.Println("追加一个切片后", cities)
   fmt.Println("追加一个元素后长度为:", len(cities), ",容量为:", cap(cities))

}

执行上述代码,输出结果如下:

追加前长度为: 3 ,容量为: 3
追加一个切片后 [顺天 应天 奉天 承天]
追加一个元素后长度为: 4 ,容量为: 6

追加切片/多个元素/合并切片

func main() {

   var cities = []string{"顺天", "应天", "奉天"}
   fmt.Println("追加前cities切片长度为:", len(cities), ",容量为:", cap(cities))

   // 定义一个切片
   var others = []string{"保定府", "河间府", "顺德府", "广平府"}

   // 追加切片第一种方式,使用 for 循环挨个追加
   for idx, item := range others {
      cities = append(cities, item)
      fmt.Println("追加", idx+1, "个切片后cities切片为:", cities)
      fmt.Println("追加", idx+1, "个元素后cities切片的长度为:", len(cities), ",容量为:", cap(cities))
   }

   var offices = []string{"顺天", "应天", "奉天"}
   fmt.Println("追加前offices切片长度为:", len(offices), ",容量为:", cap(offices))

   // 第二种方式,直接追加一个切片
   offices = append(offices, others...)
   fmt.Println("追加一个切片后,offices切片为", offices)
   fmt.Println("追加一个切片后offices切片长度为:", len(offices), ",容量为:", cap(offices))

}

执行上述代码,输出结果如下:

追加前cities切片长度为: 3 ,容量为: 3
追加 1 个切片后cities切片为: [顺天 应天 奉天 保定府]
追加 1 个元素后cities切片的长度为: 4 ,容量为: 6
追加 2 个切片后cities切片为: [顺天 应天 奉天 保定府 河间府]
追加 2 个元素后cities切片的长度为: 5 ,容量为: 6
追加 3 个切片后cities切片为: [顺天 应天 奉天 保定府 河间府 顺德府]
追加 3 个元素后cities切片的长度为: 6 ,容量为: 6
追加 4 个切片后cities切片为: [顺天 应天 奉天 保定府 河间府 顺德府 广平府]
追加 4 个元素后cities切片的长度为: 7 ,容量为: 12
追加前offices切片长度为: 3 ,容量为: 3
追加一个切片后,offices切片为 [顺天 应天 奉天 保定府 河间府 顺德府 广平府]
追加一个切片后offices切片长度为: 7 ,容量为: 7

直接追加(合并)一个切片的格式是 offices = append(offices, others...),切片参数后面有 ...,与追加单个元素的格式不同。

删除元素

Go 语言中定义删除切片的方法,但是我们可以使用 append 追加切片的方式来实现元素的删除

func main() {

   var cities = []string{"顺天", "应天", "奉天", "苏州府", "杭州府", "平凉府"}
   fmt.Println("删除元素前cities切片长度为:", len(cities), ",容量为:", cap(cities))

   // 删除应天
   fmt.Println("cities[:1]表示", cities[:1])
   fmt.Println("cities[2:]表示", cities[2:])
   cities = append(cities[:1], cities[2:]...)
   fmt.Println("合并cities[:1]和cities[2:]得到", cities)
   fmt.Println("删除元素后cities切片长度为:", len(cities), ",容量为:", cap(cities))

   // 删除多个元素
   cities = append(cities[:1], cities[4:]...)
   fmt.Println("再删除3个元素后", cities)
   fmt.Println("再删除3个元素后cities切片长度为:", len(cities), ",容量为:", cap(cities))

}

执行上述代码,输出结果如下:

删除元素前cities切片长度为: 6 ,容量为: 6
cities[:1]表示 [顺天]
cities[2:]表示 [奉天 苏州府 杭州府 平凉府]
合并cities[:1]和cities[2:]得到 [顺天 奉天 苏州府 杭州府 平凉府]
删除元素后cities切片长度为: 5 ,容量为: 6
再删除3个元素后 [顺天 平凉府]
再删除3个元素后cities切片长度为: 2 ,容量为: 6

这种删除方式起始就是将要删除的单个元素或者连续的元素片段的前后两个元素片段合并就得到新的切片,并且不包含要删除的元素

image.png

二、字典 Map

字典的定义

Map 是一种无序的 Key Value 类型的数据结构,Go 语言中的 Map 是引用数据类型,必须初始化才能使用

map[KeyType]ValueType
  • KeyType:字典中键的类型
  • ValueType:字典中值的类型

Map 类型的变量初始值为 nil,需要使用 make() 函数来分配内存

判断字典的键或者通过指定的键获取值

func main() {

   var info = map[string]string{
      "name":    "tony",
      "address": "NYC",
   }

   fmt.Println(info)

   // 判断指定的键是否存在
   val, isExist := info["age"]

   fmt.Println(val, isExist)

   val, isExist = info["name"]

   fmt.Println(val, isExist)
}

执行上述代码,输出结果如下:

map[address:NYC name:tony]
 false
tony true

Go 中通过 Key 获取 Value 的方式是通过 Map['Key'] 的方式获取的,此时会返回两个值一个是Key 对应的 Value 以及一个 Bool 值,当 Key 存在时返回对应的 Value 并且布尔值为 true,如果 Key 不存在,返回的 Value 为空,布尔值为 false。

删除字典中的键值对

Go 中内置的 delete 函数可以通过传入一个键来删除 Map 中的指定的键值对

func main() {

   var info = map[string]string{
      "name":    "tony",
      "address": "NYC",
   }

   fmt.Println(info)

   // 删除 name 键值对
   delete(info, "name")
   fmt.Println(info)

}

执行上述代码,输出结果如下:

map[address:NYC name:tony]
map[address:NYC]

遍历字典

遍历字典可以使用 for + range 函数的方式,与遍历切片、字符串的方式一致

func main() {

   var info = map[string]string{
      "name":    "tony",
      "address": "NYC",
   }

   // 遍历 Key,Value
   for k, v := range info {
      fmt.Println(k, v)
   }

   // 只遍历 Key
   for k := range info {
      fmt.Println(k)
   }

}

执行上述代码,输出结果如下:

name tony
address NYC
name
address

本文正在参加技术专题18期-聊聊Go语言框架