Go入门(2)| 豆包MarsCode AI 刷题

52 阅读5分钟

容器

数组

var 数组变量名 [数组长度]Type

数组长度也是数组类型的一部分,[3]int 和 [4]int 是两种不同的数组类型

默认情况下,数组每个元素都会被初始化为元素类型对应的零值,也可以自己初始化数组var a [3]int = {1,2,3}

q := [...]int{1,2,3}表示数组长度根据初始化

a = [3]int{1:2,2:-1}可以这样为指定索引初始化

如果两个数组类型相同(包括数组的长度,数组中元素的类型)的情况下,我们可以直接通过较运算符(==!=)来判断两个数组是否相等

slice

slice 是一个拥有相同元素的可变长度序列。 slice 看起来与数组很像,但本质上不同。slice依赖于数组

有三个属性

  1. 底层数组指针(指针指向数组的某个元素,不一定是第一个,这是slice可以访问的第一个元素,对切片的修改会影响底层数组)
  2. 长度
  3. 容量

每个切片指向一个底层数组,数组容量够用就添加新增元素,不够用切片会扩容,此时切片指向的底层数组也会更换

切片默认指向一段连续内存区域,可以是数组,也可以是切片本身。

从连续内存区域生成切片是常见的操作,格式如下:

slice[开始位置:结束位置] //包头不包尾

从数组生成新切片

var a  = [3]int{1, 2, 3}
fmt.Println(a, a[1:2])
a[:] // 生成的切片将表示和原切片一致的切片

直接声明新的切片

var name []Type

切片是动态结构,只能和nil比较,不能互相比较,声明新的切片后,可以使用 append() 函数向切片中添加元素。

使用make()构造切片

make([]Type,size,cap)

cap的设定不影响size,只是能提前分配空间降低多次分配空间造成的性能问题

func main() {
	a := make([]int,3,6)
	// a[0] = 1
	// a[1] = 2
	// a[2] = 3
	a = append(a,1)
	a = append(a,2)
	a = append(a,3)
	a = append(a,4)
	fmt.Println(a)   // [0 0 0 1 2 3]
	fmt.Println(len(a),cap(a))   // 7 12
}

可看出切片容量不够会扩容两倍

使用 make() 函数生成的切片一定发生了内存分配操作,但给定开始与结束位置(包括切片复位)的切片只是将新的切片结构指向已经分配好的内存区域,设定开始与结束位置,不会发生内存分配操作。

Go语言的内建函数 append() 可以为切片动态添加元素,代码如下所示:

var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包

a = append(a,1,2,3) // 在切片尾部添加元素
a = append([]int{1,2,3},a...) // 在切片头部添加元素

在切片开头添加元素一般都会导致内存的重新分配,而且会导致已有元素全部被复制 1 次,因此,从切片的开头添加元素的性能要比从尾部追加元素的性能差很多。

通过append函数的组合,可以实现在切片中插入元素

func main() {
	a := make([]int,3,6)
	i := 3
	x := 9
	a = append(a[:i],append([]int{x},a[i:]...)...)
	fmt.Println(a)  // [0 0 0 9]
}

range关键字

可以配合for来遍历切片,数组,字符串等

关键字 range 会返回两个值,第一个值是当前迭代到的索引位置,第二个值是该位置对应元素值的一份==副本==

map

map 是引用类型,可以使用如下方式声明:

var mapName map[keyType]valueType

make(map[KeyType]ValueType)

使用函数 len() 可以获取 map 中 pair 的数目

使用for range遍历同时获取键和值

map是无序的

delete(map,键):从map中删除键值对

sync.Map

并发环境中使用的map

  • 不能使用map的方式进行取值和设置等操作,调用他的方法进行操作,Store 表示存储,Load 表示获取,Delete 表示删除。
  • 使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。
package main

import (
      "fmt"
      "sync"
)

func main() {

    var scene sync.Map

    // 将键值对保存到sync.Map
    scene.Store("greece", 97)
    scene.Store("london", 100)
    scene.Store("egypt", 200)

    // 从sync.Map中根据键取值
    fmt.Println(scene.Load("london"))

    // 根据键删除对应的键值对
    scene.Delete("london")

    // 遍历所有sync.Map中的键值对
    scene.Range(func(k, v interface{}) bool {

        fmt.Println("iterate:", k, v)
        return true
    })

}

list

在Go语言中,列表使用 container/list 包来实现,内部的实现原理是双链表,列表能够高效地进行任意位置的元素插入和删除操作。

列表与切片和 map 不同的是,列表并没有具体元素类型的限制,因此,列表的元素可以是任意类型,这既带来了便利,也引来一些问题,例如给列表中放入了一个 interface{} 类型的值,取出值后,如果要将 interface{} 转换为其他类型将会发生宕机。

创建

import "container/list"

var l1 list.List
l := list.New()

操作

列表插入函数的返回值会提供一个 *list.Element 结构,这个结构记录着列表元素的值以及与其他节点之间的关系等信息,从列表中删除元素时,需要用到这个结构进行快速删除。

import (
	"container/list"
	"fmt"
)

func main() {
	l := list.New()

	ele := l.PushBack("back")
	element := l.PushFront("first")

	l.InsertAfter(22, element)
	l.InsertBefore(33, ele)

	l.Remove(ele) // 删除back

	for i := l.Front(); i != nil; i = i.Next() {
		fmt.Println(i.Value)
	}
}