[Go语言基础语法 (三) | 青训营笔记]

56 阅读6分钟

六、常量

c++一样常量使用const修饰

 const (
     a = 1
     b
     c = 2
     d
 )
 ​
 fmt.Println(a, b, c, d) // "1 1 2 2"

常量生成器iota

常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。

在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一

下面是来自time包的例子,它首先定义了一个Weekday命名类型,然后为一周的每天定义了一个常量,从周日0开始。在其它编程语言中,这种类型一般被称为枚举类型。

 type Weekday int
 ​
 const (
     Sunday Weekday = iota
     Monday
     Tuesday
     Wednesday
     Thursday
     Friday
     Saturday
 )

周日将对应0,周一为1,如此等等。

七、复合数据类型

1.数组

 //定长数组
 var q [number]int
 //长度不固定数组
 var q [...]int{1, 2, 3} // 长度由元素个数决定

定义了一个含有100个元素的数组r,最后一个元素被初始化为-1,其它元素都是用0初始化。

 r := [...]int{99: -1}

数组可以用==比较元素是否全部相等

2.Slice

切片,底层是数组实现的,变长数组

Slice底层

image-20230317165515101

一个指针指向一个数组,剩下两个元素一个是len,一个是cap

注意 绝对不要用指针指向 slice。切片本身已经是一个引用类型,所以它本身就是一个指针!!

append函数

用于向Slice中追加元素

 var runes []rune
 for _, r := range "Hello, 世界" {
     runes = append(runes, r)
 }
 fmt.Printf("%q\n", runes) // "['H' 'e' 'l' 'l' 'o' ',' ' ' '世' '界']"

array = append(array, 元素)

必须这样写,

当我们数组长度不够的时候就会创建一个新的更大的切片,然后把原来的切片复制过去,这个过程会消耗资源,当切片一直被复制和追加的时候整个程序的性能就会降低,因此我们创建的时候就需要考虑好我们所用切片的大小,尽量去避免切片的扩大

len和cap

len(返回长度),cap(返回最大长度)

New和make的区别

  • new (T) 为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 * T 的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体(参见第 10 章);它相当于 &T{}。
  • make(T) 返回一个类型为 T 的初始值,它只适用于 3 种内建的引用类型:切片、map 和 channel。

换言之,new 函数分配内存,make 函数初始化;下图给出了区别:

image-20230317165804551

bytes包

这是一个长度可变的 bytes 的 buffer,提供 Read 和 Write 方法,读写长度未知的 bytes 最好使用 buffer。

Buffer 可以这样定义:var buffer bytes.Buffer。

通过Buffer串联字符串

这时Buffer类似于Java中的StringBuilder

 var buffer bytes.Buffer
 for {
     if s, ok := getNextString(); ok { // method getNextString() not shown here
         buffer.WriteString(s)
     } else {
         break
     }
 }
 fmt.Print(buffer.String(), "\n")

字符串、数组和切片的应用

字符串生成字节切片

假设 s 是一个字符串(本质上是一个字节数组),那么就可以直接通过 c := []byte(s) 来获取一个字节数组的切片 c

获取字符串的某一部分

使用 substr := str[start:end] 可以从字符串 str 获取到从索引 start 开始到 end-1 位置的子字符串。同样的,str[start:] 则表示获取从 start 开始到 len(str)-1 位置的子字符串。而 str[:end] 表示获取从 0 开始到 end-1 的子字符串。

修改字符串中某个字符的值

无法直接修改,必须先把字符串转化为字节切片,然后通过字节切片修改,最后赋值给字符串

 s := "hello"
 c := []byte(s)
 c[0] = 'c'
 s2 := string(c) // s2 == "cello"

3.Map

3.1 概念

哈希表是一种巧妙并且实用的数据结构。它是一个无序的key/value对的集合,其中所有的key都是不同的,然后通过给定的key可以在常数时间复杂度内检索、更新或删除对应的value。

map的value可以是任何类型包括函数

3.1.1 创建map

只能使用make

创建map,使用内置的make函数

 ages := make(map[string]int) // mapping from strings to ints
 ​
 // 或者 这样声明
 mapLit = map[string]int{"one": 1, "two": 2}
3.1.2 使用函数作为value
 package main
 import "fmt"
 ​
 func main() {
     mf := map[int]func() int{
         1: func() int { return 10 },
         2: func() int { return 20 },
         5: func() int { return 50 },
     }
     fmt.Println(mf)
     // map[1:0x10903be0 5:0x10903ba0 2:0x10903bc0]
 }
3.1.3 使用切片作为value

当我们一个key想对应多个value时就可以用这个方法

 mp1 := make(map[int][]int)
 mp2 := make(map[int]*[]int)

3.2 使用方法

3.2.1添加元素

添加元素和c++添加方式相同

3.2.2 删除元素

删除元素使用delete函数

 delete(ages, "alice") // remove element ages["alice"]
3.2.3 判断键值对是否存在

Go语言,map的下标语法将产生两个值;第二个是一个布尔值,用于报告元素是否真的存在。布尔变量一般命名为ok,特别适合马上用于if条件判断部分。

 if age, ok := ages["bob"]; !ok { /* ... */ }
3.2.4 遍历map(for-range)

遍历map中的所有kv对

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

这样遍历的方法是乱序的,每次的结果都不一样,如果想让结果一样那么我们需要使用sort函数对key进行排序

 // the telephone alphabet:
 package main
 import (
     "fmt"
     "sort"
 )
 ​
 var (
     barVal = map[string]int{"alpha": 34, "bravo": 56, "charlie": 23,
                             "delta": 87, "echo": 56, "foxtrot": 12,
                             "golf": 34, "hotel": 16, "indio": 87,
                             "juliet": 65, "kili": 43, "lima": 98}
 )
 ​
 func main() {
     fmt.Println("unsorted:")
     for k, v := range barVal {
         fmt.Printf("Key: %v, Value: %v / ", k, v)
     }
     keys := make([]string, len(barVal))
     i := 0
     for k, _ := range barVal {
         keys[i] = k
         i++
     }
     sort.Strings(keys)
     fmt.Println()
     fmt.Println("sorted:")
     for _, k := range keys {
         fmt.Printf("Key: %v, Value: %v / ", k, barVal[k])
     }
 }
 ​
 // 输出
 // unsorted:
 // Key: bravo, Value: 56 / Key: echo, Value: 56 / Key: indio, Value: 87 / Key: juliet, Value: 65 / Key: alpha, Value: 34 / Key: charlie,    // Value: 23 / Key: delta, Value: 87 / Key: foxtrot, Value: 12 / Key: golf, Value: 34 / Key: hotel, Value: 16 / Key: kili, Value: 43 / Key: // lima, Value: 98 /
 // sorted:
 // Key: alpha, Value: 34 / Key: bravo, Value: 56 / Key: charlie, Value: 23 / Key: delta, Value: 87 / Key: echo, Value: 56 / Key: foxtrot,  // Value: 12 / Key: golf, Value: 34 / Key: hotel, Value: 16 / Key: indio, Value: 87 / Key: juliet, Value: 65 / Key: kili, Value: 43 / Key: // lima, Value: 98 /

4.JSON

JSON格式声明

在结构体后面加上JSON:jsonName

Go语言中把结构体转化为JSON使用json.Marshal函数

 data, err := json.Marshal(movies)
 if err != nil {
     log.Fatalf("JSON marshaling failed: %s", err)
 }
 fmt.Printf("%s\n", data)

Marshal函数返回一个编码后的字节slice,包含很长的字符串,并且没有空白缩进;我们将它折行以便于显示:

 [{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingrid Bergman"]},{"Title":"Cool HandLuke","released":1967,"color":true,"Actors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"Actors":["SteveMcQueen","Jacqueline Bisset"]}]

另外一个函数json.MarshalIndent会产生整齐的缩进

 data, err := json.MarshalIndent(movies, "", "    ")
 if err != nil {
     log.Fatalf("JSON marshaling failed: %s", err)
 }
 fmt.Printf("%s\n", data)

返回这样的格式

 [
     {
         "Title": "Casablanca",
         "released": 1942,
         "Actors": [
             "Humphrey Bogart",
             "Ingrid Bergman"
         ]
     },
     {
         "Title": "Cool Hand Luke",
         "released": 1967,
         "color": true,
         "Actors": [
             "Paul Newman"
         ]
     },
     {
         "Title": "Bullitt",
         "released": 1968,
         "color": true,
         "Actors": [
             "Steve McQueen",
             "Jacqueline Bisset"
         ]
     }
 ]