六、常量
和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底层
一个指针指向一个数组,剩下两个元素一个是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 函数初始化;下图给出了区别:
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"
]
}
]