1

Go 基础数据结构

 2 years ago
source link: https://wnanbei.github.io/post/go-%E5%9F%BA%E7%A1%80%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Go 基础数据结构 arrayslicemap 的声明与使用。

array

数组是同一类型元素的集合。

长度固定,不允许数组混合不同类型的元素。

var a [3]int
  • 数组中的所有元素会被自动赋值为数据类型的零值。
  • 数组的索引从 0 开始。
  • 不同大小的数组视为不同类型。
  • Go 中的数组是值类型而不是引用类型,当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。

声明时赋值:

var a = [3]int{12, 78, 50}
a := [3]int{12, 78, 50}
a := [5]int{1:12, 3:78}
a := [...]int{12, 78, 50}  // 自动计算长度
a := [3][2]string{
    {"lion", "tiger"},
    {"cat", "dog"},
    {"pigeon", "peacock"},
}

slice

切片可以理解为一个可变长度的数组。

切片本质上是对数组建立的方便、灵活且功能强大的封装(Wrapper)。

切片本身不存储任何数据,数据存储在底层的数组中,切片只是对数组的引用。对切片所做的任何修改都会反映在底层数组中。

c := []int{6, 7, 8}

使用已有数组创建切片。

a := [5]int{76, 77, 78, 79, 80}
var b []int = a[1:4]

使用 make 创建切片,三个参数是类型、长度、容量,容量是可选参数,默认值与长度相同。

长度不为 0 时,将会自动填充相应数量的默认值。

c := make([]int, 5, 5)

使用 make 创建空切片时,一般长度使用 0,但是最好指定容量信息,避免后续追加内容时容量不足,造成频繁的内存分配。

切片还有 nil 切片和空切片,它们的长度和容量都是 0,但是它们指向底层数组的指针不一样

  • nil 切片意味着指向底层数组的指针为 nil,表示切片不存在

    var nilSlice []int  // nil 切片
    
  • 而空切片对应的指针是个地址,一般用来表示这是一个空集合,没有内容

    slice := []int{}  // 空切片
    

长度和容量

  • 长度使用 len() 查看,是切片中元素的数量。访问超过长度的索引值会报错。
  • 容量使用 cap() 查看,是指创建切片时,底层数组的元素数量。容量必须大于等于长度。

使用append()方法

var a []int
append(a, 10)
append(a, 20, 30, 40)

veggies := []string{"potatoes", "tomatoes", "brinjal"}
fruits := []string{"oranges", "apples"}
food := append(veggies, fruits...)

使用 append() 方法时,如果添加元素后的数量超过了切片的容量,那么 Go 会自动创建一个容量翻倍的底层数组,并将原有数组的数据复制到新的数组中,这样就实现了切片的动态长度。

注意:最好预先定好切片的大概长度,避免频繁的重新分配数组,浪费系统性能。

切片中保存的是对底层数组的引用,所以赋值给其他变量后,修改的依然是原数组。

如果有对某个数组的切片存在,那么原数组就不会被垃圾回收。

使用 copy() 函数可以生成一个与原切片无关的切片副本。

countries := []string{"USA", "Singapore", "Germany", "India", "Australia"}
neededCountries := countries[:len(countries)-2]
countriesCpy := make([]string, len(neededCountries))
copy(countriesCpy, neededCountries)
  • Map 与切片相同,是引用类型

  • Map 之间不能使用 == 操作符进行判断,== 只能用来检查 Map 是否为 nil

表示方式为 map[key type]value type,如 map[string]int

m := map[string]int{}
var m = map[string]int{"hunter":12,"tony":10}
m := make(map[string]int)

与切片相同,使用 make 创建 map 时,最好指定容量信息,避免后续追加内容时容量不足,造成频繁的内存分配

files, _ := ioutil.ReadDir("./files")

m := make(map[string]os.FileInfo, len(files))
for _, f := range files {
    m[f.Name()] = f
}

取值方式为 map[key],如果取一个不存在的值的话,那么会返回相应的数据类型的零值。

如果我们希望知道 Map 中到底存不存在这个值,我们可以使用以下语法

value, ok := map[key]

删除 Map 中的元素可以使用 delete(map, key) 函数,此函数没有返回值。

delete(person, "steve")

New 和 Make 区别

Go 语言中 newmake 是内建的两个函数,主要用来创建分配类型内存。

通常的声明方式

var i int
var s string
  • 声明值类型变量时,默认值是他们的零值
  • 声明引用类型时,类型的零值是 nil

而直接声明引用类型,会造成引用类型的内存空间没有分配,无法存储数据,例如以下代码会报错:

var i *int
*i = 10
fmt.Println(*i)

new() 函数主要用于为变量分配内存,并初始化为零值。

它只接受一个类型作为参数,分配好内存后,返回一个指向该类型内存地址的指针。同时把分配的内存值置为零,也就是类型的零值。

var i *int
i = new(int)
*i = 10
fmt.Println(*i)

struct 可以用此方法将 struct 内部的字段初始化,例如下面这个 user 类型的 lock 字段,不需要额为的初始化了。

new() 也等价于 u := user{}

type user struct {
	lock sync.Mutex
	name string
	age int
}

u:=new(user)
u.lock.Lock()
u.name = "张三"
u.lock.Unlock()

make() 函数主要用于为特定类型分配内存:SliceMapChan

  • 因为这三种类型就是引用类型,所以没有必要返回他们的指针。

  • 由于是引用类型,所以必须得初始化,但不是置为零值,这是和 new 不一样的。

  • 二者都是内存的分配(堆上)
  • make 只用于 slice、map、chan 的初始化(非零值)
  • new 用于类型的内存分配,并且内存置为零值

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK