数据类型
package main
import "fmt"
func main() {
var a = "initial" fmt.Println(a) // initial
var b, c int = 1, 2 fmt.Println(b, c) // 1 2
var d = true fmt.Println(d) // true
var e int fmt.Println(e) // 0
f := "apple" fmt.Println(f) // apple}package main
import ( "fmt" "math")
const s string = "constant"
func main() { fmt.Println(s) // constant
const n = 500000000
const d = 3e20 / n fmt.Println(d) // 6e+11
fmt.Println(int64(d)) // 600000000000
fmt.Println(math.Sin(n)) // -0.28470407323754404}基本数据类型
Section titled “基本数据类型”// byte 就是字节,本质是uint8,一个byte 1个字节type byte = uint8// rune就是字符,本质是int32,一个rune 4个字节// 接近其他语言的char
type rune = int32string
Section titled “string”package main
import ( "strings" "unicode/utf8")
// 字符串等价于 []byteconst s = "hello world"
func main() { // 一般用于短的 println("Hello, World!") // 一般用于长的,多行的 println(`Hello, World!我是第二行`)
// 获取字符串长度 // 获取的是字节(byte)长度,与编码无关 println(len("你好")) // 6 // 获取字符长度 println(utf8.RuneCountInString("你好")) // 2
// 对应的操作包在 strings 下 strings.Cut("hello world", " ")
// 注意:string只能和string拼接,不能和其他类型拼接}- 数组是固定长度的
- 具有相同数据类型元素的有序集合
- 值类型(当数组被赋值给一个新变量或传递给函数时,会创建整个数组的副本)
package main
import "fmt"
func main() { // 声明数组,但不初始化 var a [5]int fmt.Println("emp:", a) // emp: [0 0 0 0 0]
a[4] = 100 fmt.Println("set:", a) // set: [0 0 0 0 100] fmt.Println("get:", a[4]) // get: 100
fmt.Println("len:", len(a)) // len: 5
// 声明数组并初始化 b := [5]int{1, 2, 3, 4, 5} fmt.Println("dcl:", b) // dcl: [1 2 3 4 5]
// 声明数组,长度由编译器计算 c := [...]int{1, 2, 3, 4, 5}
var twoD [2][3]int for i := 0; i < 2; i++ { for j := 0; j < 3; j++ { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) // 2d: [[0 1 2] [1 2 3]]}- 存储固定大小的数据集合(如一周的温度数据)
- 内存敏感场景,数组在栈上分配
- 加密或编码等需要精确控制内存布局的场景
- 长度是动态的
- 切片是引用类型,包含三个组件:底层数组指针,当前切片长度,切片容量
package main
import "fmt"
func main() { // 声明切片 var slice1 []int
// make初始化 make([]T, len, cap) cap不传则和len一样 // len(slice) 获取切片长度 // cap(slice) 获取切片容量 // 初始化一个长度为3的切片 s := make([]string, 3) fmt.Println("emp:", s) // emp: [ ]
s[0] = "a" s[1] = "b" s[2] = "c" fmt.Println("set:", s) // set: [a b c] fmt.Println("get:", s[2]) // get: c
fmt.Println("len:", len(s)) // len: 3
// append 追加元素 // 如果添加元素没有超过容量限制,不会发生扩容 // 如果添加元素超过容量限制,会发生扩容,扩容规则是翻倍 s = append(s, "d") s = append(s, "e", "f") fmt.Println("apd:", s)
c := make([]string, len(s)) // apd: [a b c d e f] copy(c, s) fmt.Println("cpy:", c) // cpy: [a b c d e f]
/*
子切片 数组和切片都可以通过[start:end]的方式来获取子切片 1. arr[start:end] 获取[start:end)之间的元素 2. arr[:end] 获取[0:end)之间的元素 3. arr[start:] 获取[start:len(arr))之间的元素
注意:左闭右开,包含左边,不包含右边
*/
l := s[2:5] fmt.Println("sl1:", l) // sl1: [c d e]
l = s[:5] fmt.Println("sl2:", l) // sl2: [a b c d e]
l = s[2:] fmt.Println("sl3:", l) // sl3: [c d e f]
t := []string{"g", "h", "i"} fmt.Println("dcl:", t) // dcl: [g h i]
// slice的len和cap不一定相等,len代表这个slice目前有多少个元素,cap代表这个切片能放多少个元素 // 初始化一个4个元素的切片 s1 := []int{1, 2, 3, 4, 5, 6} fmt.Printf("s1: %v\n", s1) // s1: [1 2 3 4 5 6] fmt.Printf("len(s1): %v\n", len(s1)) // len(s1): 6 fmt.Printf("cap(s1): %v\n", cap(s1)) // cap(s1): 6
// 初始化一个长度为3,容量为10的切片 s2 := make([]int, 3, 10) fmt.Printf("s2: %v\n", s2) // s2: [0 0 0] fmt.Printf("len(s2): %v\n", len(s2)) // len(s2): 3 fmt.Printf("cap(s2): %v\n", cap(s2)) // cap(s2): 10
// 最佳实践 // 初始化一个空切片,容量为10 slice := make([]int, 0, 10)
twoD := make([][]int, 3) for i := 0; i < 3; i++ { innerLen := i + 1 twoD[i] = make([]int, innerLen) for j := 0; j < innerLen; j++ { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) // 2d: [[0] [1 2] [2 3 4]]}
// 共享底层func shareSlice() { s1 := []int{1, 2, 3, 4} s2 := s1[2:] fmt.Printf("s1: %v, len: %d, cap: %d \n", s1, len(s1), cap(s1)) // s1: [1 2 3 4], len: 4, cap: 4 fmt.Printf("s2: %v, len: %d, cap: %d \n", s2, len(s2), cap(s2)) // s2: [3 4], len: 2, cap: 2
s2[0] = 99 fmt.Printf("s1: %v, len: %d, cap: %d \n", s1, len(s1), cap(s1)) // s1: [1 2 99 4], len: 4, cap: 4 fmt.Printf("s2: %v, len: %d, cap: %d \n", s2, len(s2), cap(s2)) // s2: [99 4], len: 2, cap: 2
s2 = append(s2, 200) fmt.Printf("s1: %v, len: %d, cap: %d \n", s1, len(s1), cap(s1)) // s1: [1 2 99 200], len: 4, cap: 4 fmt.Printf("s2: %v, len: %d, cap: %d \n", s2, len(s2), cap(s2)) // s2: [99 200], len: 2, cap: 2
s2[1] = 1999 fmt.Printf("s1: %v, len: %d, cap: %d \n", s1, len(s1), cap(s1)) // s1: [1 2 99 1999], len: 4, cap: 4 fmt.Printf("s2: %v, len: %d, cap: %d \n", s2, len(s2), cap(s2)) // s2: [99 1999], len: 2, cap: 2}- 动态数据集合(如读取未知长度的文件内容)
- 复用内存,避免复制(如截取日志子集)
- 实现栈或队列数据结构
- 高性能批量处理
array vs slice
Section titled “array vs slice”| array | slice | |
|---|---|---|
| 直接初始化 | 支持 | 支持 |
| make | 不支持 | 支持 |
| 访问元素 | arr[i] | s[i] |
| len | 长度 | 已有元素个数 |
| cap | 长度 | 容量 |
| append | 不支持 | 支持 |
| 是否可以扩容 | 不可以 | 可以 |
总结:遇事不决用切片,基本不会出错
如何理解切片
Section titled “如何理解切片”最直观的对比:arraylist,即基于数组的list的实现,切片的底层也是数组
跟arraylist的区别:
- 切片操作是有限的,不可以随机增删(没有add,delete方法,可以自己实现)
- 只有append操作
- 切片支持子切片,和原本切片是共享底层数组
数组和切片有什么区别
Section titled “数组和切片有什么区别”- 数组的大小是固定的,切片不是,切片可以动态扩容
- 切片和子切片共享底层,部分修改相互之间会有影响
- 需要注意切片的扩容策略,扩容后会重新分配底层数组,不再共享
package main
import "fmt"
func main() { // 声明nil map var m1 map[string]int
// 字面量创建 m2:= map[string]int{ "A": 1, "B": 2, }
// 使用make创建一个map m := make(map[string]int)
// 赋值 m["k1"] = 7 m["k2"] = 13
fmt.Println("map:", m) // map: map[k1:7 k2:13]
// 取值 v1 := m["k1"] fmt.Println("v1: ", v1) // v1: 7
fmt.Println("len:", len(m)) // len: 2
delete(m, "k2") fmt.Println("map:", m) // map: map[k1:7]
// 取值,判断是否存在,后面的ok会告诉map中是否存在这个key value, ok := m["k2"] fmt.Println("ok:", ok) // ok: false
// 初始化一个map n := map[string]int{"foo": 1, "bar": 2} fmt.Println("map:", n) // map: map[foo:1 bar:2]
// 遍历 for k, v := range m { fmt.Printf("key: %s, value: %d\n", k, v) }}package main
import ( "fmt" "sort" "sync")
func main() { // 作为集合使用 uniqueIds := make(map[int]bool) ids := []int{1, 2, 2, 3, 3, 4, 5, 5} for _, id := range ids { uniqueIds[id] = true } fmt.Println(len(uniqueIds)) // 5
// 键值反转 originMap := map[string]int{ "A": 1, "B": 2, "C": 3, } reversedMap := make(map[int]string) for key, value := range originMap { reversedMap[value] = key } fmt.Println(reversedMap) // map[1:A 2:B 3:C]
// map是无序的键值对 ageMap := map[string]int{ "Alice": 18, "Bob": 20, "Coco": 19, } keys := make([]string, 0, len(ageMap)) for key := range ageMap { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { fmt.Printf("%s: %d\n", key, ageMap[key]) }
// sync.Map var syncMap sync.Map syncMap.Store("A", 1) syncMap.Store("B", 2) fmt.Println(syncMap.Load("A")) // 1 true syncMap.Delete("A") syncMap.Range(func(key, value interface{}) bool { fmt.Println(key, value) // B 2 return true })}