1. 数组(array)

1.1 概念

数字是具有相同 唯一类型 的一组已编号且长度固定的数据项序列(这是一种同构的数据结构);这种类型可以是任意的原始类型例如整型、字符串或自定义类型。数组长度必须是一个常量表达式,并且必须是一个非负整数。数组长度也是数组类型的一部分,所以[5]int和[10]int是属于不同类型的。

数组元素可以通过 索引 (位置)来读取(或者修改),索引从 0 开始,第一个元素索引为0,第二个索引为 1,以此类推。元素的数目,也称为长度或者数组大小必须是固定的并且在声明该数组时就给出;数组长度最大为2Gb。

声明的格式:

  1. var identifier [len]type

例如:

  1. var arr1 [5]int

复合数据类型 - 图1

在内存中的结构是;

每个元素是一个整型值,当声明数组时所有的元素都会被自动初始化为默认值 0。

arr1 的长度是 5,索引范围从 0 到 len(arr1)-1。

第一个元素是 arr1[0],第三个元素是 arr1[2];总体来说索引 i 代表的元素是 arr1[i],最后一个元素是 arr1[len(arr1)-1]。

对索引项为 i 的数组元素赋值可以这么操作:arr[i] = value,所以数组是 可变的。

只有有效的索引可以被使用,当使用等于或者大于 len(arr1) 的索引时:如果编译器可以检测到,会给出索引超限的提示信息;如果检测不到的话编译会通过而运行时会 panic:

  1. runtime error: index out of range

1.2 数组常量

如果数组值已经提前知道了,可以通过 数组常量 的方法来初始化数组

  1. 指定数组长度:[length]type{v1, v2, …, vlength}var arrAge = [5]int{18, 20, 15, 22, 16}
    注意:[5]int可以从左边起开始忽略:[10]int{1, 2, 3} 这是一个有10个元素的数组,除了前三个元素外其他元素都为0.
  2. 使用初始化元素数量推到数组长度: […]type{v1, v2, …, vlength}var arrLazy = [...]int{5, 6, 7, 8, 22}
    … 可同样可以忽略,从技术上说它们其实变化成了切片。
  3. 对指定位置元素进行初始化: [length]type{im:vm,…,in:in}``` var arrKeyValue = [5]string{3: “Chris”, 4: “Ron”}
    1. <br />只有索引 3 和 4 被赋予实际的值,其他元素都被设置为空的字符串,所以输出结果为:
    Person at 0 is Person at 1 is Person at 2 is Person at 3 is Chris Person at 4 is Ron ```

1.3 操作

  1. 关系运算 ==、!=var nums [2]int = [...]int{100, 88} fmt.Println(nums == scores)

  2. 获取数组长度,使用len函数可以获取数组的长度fmt.Println(len(nums))

  3. 访问&修改
    通过对编号对数组元素进行访问和修改,元素的编号从左到右依次为:0, 1, 2, …, n(n 为数组长度-1)``` fmt.Println(nums[0]) fmt.Println(nums[1])

nums[0] = 101 nums[1] = 102 fmt.Println(nums)

  1. 4. 遍历<br />由于索引的存在,遍历数组的方法自然就是使用for结构:
  2. - 通过 for 初始化数组项
  3. - 通过 for 打印数组元素
  4. - 通过 for 一次处理元素
  5. for_arrays.go:

package main

import “fmt”

func main() { var arr1 [5]int

  1. for i:=0;i<len(arr1);i++{
  2. arr1[i]=i *2
  3. }
  4. for i:=0;i<len(arr1);i++{
  5. fmt.Printf("Array at index %d is %d\n",i,arr1[i])
  6. }

}

  1. 输出:

Array at index 0 is 0 Array at index 1 is 2 Array at index 2 is 4 Array at index 3 is 6 Array at index 4 is 8

  1. for 循环中的条件非常重要:`i < len(arr1)`, 如果写成 `i <= len(arr1)` 的话会产生越界错误。<br />也可以使用for-range的生成方式:

for i, v := range arr1 { fmt.Printf(“Array at index %d is %d\n”,i,v) }

  1. <a name="091b0b61"></a>
  2. ### 1.4 多维数组
  3. 数组通常是一维的,但是可以用来组装成多维数组,例如:`[3][5]int, [2][2][2]float64`
  4. 内部数组总是长度相同的,Go语言的多维数组是矩形式的

package main

import “fmt”

func main() { //[length]type // type [2]int // 二维数组 var ms [3][2]int

  1. fmt.Printf("%T\n", ms)
  2. fmt.Println(ms)
  3. fmt.Printf("%T %v\n", ms[0], ms[0])
  4. fmt.Printf("%T %v\n", ms[0][0], ms[0][0])
  5. ms = [...][2]int{
  6. 1: [2]int{1, 2},
  7. 2: [2]int{3, 4},
  8. 0: [2]int{5, 6},
  9. }
  10. // [[5, 6], [1, 2], [3, 4]]
  11. ms[0][1] = 100
  12. fmt.Println(ms)
  13. ms[1] = [2]int{101, 102}
  14. fmt.Println(ms)
  15. for i, line := range ms {
  16. fmt.Println(i, line)
  17. for j, v := range line {
  18. fmt.Println(i, j, v)
  19. }
  20. }

}

  1. <a name="4f21fd02"></a>
  2. ### 1.5 将数组传递给函数
  3. 把一个大数组传递给函数会消耗很多内存,有两种方法可以避免这种现象:
  4. - 传递数组的指针
  5. - 使用数组的切片
  6. array_sum.go:

import “fmt”

func main() { array := [3]float64{7.0,8.5,9.1} x := Sum(&array) fmt.Printf(“The sum of the array is: %f”,x) }

func Sum(a *[3]float64) (sum float64){ for _, v:= range a { sum += v } return }

  1. 输出结果:

The sum of the array is: 24.600000

  1. 但这在Go中并不常用,通常使用切片
  2. ---
  3. <a name="ae0a1110"></a>
  4. ## 2. 切片(slice)
  5. 切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于C/C++中的数组类型,或者Python中的list类型)。
  6. 这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。
  7. 切片是可索引的,并且可以由`len()`函数获取长度。
  8. 和数组不通的是,切片的长度可以在运行时修改,最小为0,最大为相关数组的长度:**切片是一个长度可变的数组**。
  9. 切片提供了计算容量的函数`cap()`可以测量切片最长可以达到多少, 如果 s 是一个切片,cap(s) 就是从 s[0] 到数组末尾的数组长度。切片的长度永远不会超过它的容量,所以对于切片s来说该不等式永远成立:`0 <= len(s) <= cap(s)`
  10. 多个切片如果表示同一数组的片段,他们可以共享数据;因此一个切片和相关数组的其他切片是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是切片的构建块。
  11. **优点** 因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在Go代码中 切片比数组更常用。
  12. <a name="fe04245a"></a>
  13. ### 2.1 切片声明:

var identifier []type (不需要说明长度)

  1. 一个切片在未初始化之前默认为nil,长度为0
  2. <a name="ce94ff80"></a>
  3. ### 2.2 切片初始化:
  4. <a name="c6055c49"></a>
  5. #### 2.2.1 使用数组切片操作初始化:

var slice1 []type = arr1[start:end]

  1. 这表示slice1是由数组arr1 start 索引到 end-1 索引之间的元素构成的子集(切分数组,start:end 被称为 slice 表达式)。所以 slice1[0] 就等于 arr1[start]。
  2. `var slice1 []type = arr1[1:]` slice1就等于完整的arr1数组,另外一种表述方式是:`slice1 = &arr1`
  3. `arr1[2:]` `arr1[2:len(arr1)]` 相同,都包含了数组从第三个到最后的所有元素。
  4. `arr1[:3]` `arr1[0:3]` 相同,包含了从第一个到第三个元素。
  5. 去掉slice1的最后一个元素,只要`slice1 = slice1[:len(slice1)-1]`
  6. <a name="2b0e19b3"></a>
  7. #### 2.2.2 使用字面量初始化

var x = []int{}

  1. 初始化了一个空切片

var x = []int{1, 2, 3, 4, 5}

  1. **注意** 绝对不要用指针指向slice。切片本身已经是一个引用类型,所以它本身就是一个指针!!
  2. <a name="b6d869d8"></a>
  3. #### 2.2.3 使用make()函数初始化
  4. 当相关数组还没有定义时,可以使用make()函数来创建一个切片,同时创建好相关数组:`var slice1 []type = make([]type, len) <--> slice1 := make([]type, len)`。这里的 len slice 的初识长度。
  5. 定义 `s2 := make([]int, 10) --> cap(s2) == len(s2) == 10`.
  6. <a name="8df1c48c"></a>
  7. #### 2.2.4 new()和make()区别
  8. 看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。
  9. - new(T)为每个新的类型T分配一片内存,初识为0并且返回类型为*T的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体;它相当于 &T()。
  10. - make(T)返回一个类型为T的初始值,它只适用于3中内建的引用类型:切片、mapchannel
  11. 换言之,new函数分配内存,make函数初始化。
  12. <a name="a851cc2a"></a>
  13. ### 2.3 slice操作
  14. <a name="3c4ac47d"></a>
  15. #### 2.3.1 获取切片长度和容量
  16. 使用len函数可获取切片的长度,使用cap函数可获取切片容量

slice1 := make([]string, 3, 5)

fmt.Println(len(slice1), cap(slice1)) fmt.Printf(“%q\n”, slice1)

  1. <a name="3a8247df"></a>
  2. #### 2.3.2 访问和修改
  3. 通过对编号对切片元素进行访问和修改,元素的编号从左到右依次为:0,1,2,... , n(n为切片长度 - 1)

fmt.Printf(“%q, %q\n”, slice1[0], slice1[1])

slice1[0] = “abc” alice1[1] = “bcd” fmt.Printf(“%q, %q, %q\n”, slice1[0], slice1[1]) fmt.Printf(“%q\n”, slice1)

  1. <a name="04f09f78"></a>
  2. #### 2.3.3 切片
  3. slice[start:end]用于创建一个新的切片,end <= src_cap

slice1 := […]string(“a1”, “b1”, “c1”, “d1”, “e1”)

slice01 := slice1[:]

slice02 := slice1[0:3] slice03 := slice1[1:4] slice04 := slice01[1:3]

fmt.Printf(“%q\n”,slice1) fmt.Printf(“%d, %d, %d\n”, len(slice01), cap(slice01), slice01) fmt.Printf(“%d, %d, %d\n”, len(slice02), cap(slice02), slice02) fmt.Printf(“%d, %d, %d\n”, len(slice03), cap(slice03), slice03) fmt.Printf(“%d, %d, %d\n”, len(slice04), cap(slice04), slice04)

  1. 切片共享底层数组,若某个切片元素发生变化,则数组和其他有共享元素的切片也会发生变化。
  2. <a name="f5ebf897"></a>
  3. #### 2.3.4 For-range 遍历
  4. 这种构建方法可以应用于数组和切片:

for ix, value := range slice1 { … }

  1. 第一个返回值 ix 是数组或者切片的索引,第二个是在该索引位置的值;他们都是仅在for循环内部可见的局部变量。
  2. value 只是 slice1 某个索引位置的值的一个拷贝,不能用来修改 slice1 该索引位置的值。
  3. slices_forrange.go:

package main import “fmt”

func main() { seasons := []string{“Spring”, “Summer”, “Autumn”, “Winter”} for ix, season := range seasons { fmt.Printf(“Season %d is: %s\n”, ix, season) }

  1. var season string
  2. for _, season = range seasons {
  3. fmt.Printf("%s\n", season)
  4. }

}

  1. **多维切片下的for-range:**

for row := range screen { for column := range screen[row] { screen[row][column] = 1 } }

  1. <a name="6cfef6d0"></a>
  2. #### 2.3.5 切片重组(reslice)
  3. 切片创建的时候通常比相关数组小,例如:

slice1 := make([]type, start_length, capacity)

  1. 其中 `start_length` 作为切片初识长度而 `capacity` 作为相关数组的长度。
  2. 这么做的好处是我们的切片在达到容量上限后可以扩容,改变切片长度的过程称之为切片重组 replacing,做法如下:`slice1 = slice1[0:end]`,其中end是新的末尾索引(即长度)。
  3. 如:扩展1

slice1 = slice1[0:len[slice1]+1]

  1. 切片可以反复扩展直到占据整个相关数组。
  2. <a name="f6c5d8a8"></a>
  3. #### 2.3.6 切片的复制与追加
  4. 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。下面的代码描述了从拷贝切片的copy函数和向切片追加新元素的append函数。
  5. copy_append_slice.go:

package main

import “fmt”

func main() { slFrom := []int{1, 2, 3} slTo := make([]int, 10)

  1. n := copy(slTo, slFrom)
  2. fmt.Println(slTo)
  3. fmt.Printf("Copied %d elements\n", n)
  4. sl3 := []int{1, 2, 3}
  5. sl3 = append(sl3, 4, 5, 6)
  6. fmt.Println(sl3)

}

  1. `func append(s[]T, x ...T)` []T 其中append方法将0个或多个具有相同类型s的元素追加到切片后面并且返回新的切片;追加的元素必须和原切片的元素同类型。如果s的容量不足以存储新增元素,append会分配新的切片来保证已有切片元素和新增元素的存储。因此,返回的切片可能已经指向一个不同的相关的数组了,append方法总是返回成功,除非系统内存耗尽了。
  2. 将切片y追加到切片x后面,只要将第二个参数扩展成一个列表即可:`x = append(x, y...)`
  3. `func copy(dst, src []T) int` copy 方法将类型为T的切片从源地址src拷贝到目标地址dst,覆盖dst的相关元素,并且返回拷贝的元素个数。源地址和目标地址可能会有重叠。拷贝个数是srcdst的长度最小值。如果src是字符串那么元素类型就是byte。如果还想继续使用src,在拷贝结束后执行 src = dst
  4. <a name="f25935a7"></a>
  5. #### 2.3.7 从字符串生成字节切片
  6. 假设s是一个字符串(本质上是一个字节数组),那么就可以直接通过 `c := []byte(s)` 来获取一个字节的切片c。另外,还可以通过copy函数来达到相同的目的:`copy(dst []byte, src string)`
  7. 同样,还可以使用for-range来获得每个元素。
  8. for_string.go:

package main

import “fmt”

func main() { str := “hello!”

  1. for i, c := range str {
  2. fmt.Printf("%d:%c\n", i, c)
  3. }

}

  1. 输出:

0:h 1:e 2:l 3:l 4:o 5:!

  1. <a name="6a3720c2"></a>
  2. #### 2.3.8 获取字符串的某一部分
  3. 使用 `substr := str[start:end]` 可以从字符串str获取到从索引start开始到end-1位置的子字符串。
  4. `str[start:]` 则表示获取从start开始到len(str)-1位置的子字符串。
  5. 而`str[:end]` 表示获取从0开始到end-1的字符串。
  6. <a name="4bba248a"></a>
  7. #### 2.3.9 修改字符串中的某个字符
  8. Go语言中的字符串是不可变的,也就是说`str[index]` 这样的表达式是不可以被放在等号左侧的。如果尝试运行`str[i]='D'` 会得到错误:`cannot assign to str[i]`。
  9. 因此,必须先将字符串转换成字节数组,然后再通过修改数组中的元素值来达到修改字符串的目的,最后将字节数组转换回字符串格式。
  10. 例如:

s := ‘hello’ c := []byte(s) c[0] = ‘c’ s2 := string(c) // s2 == ‘cello’

  1. <a name="a7f65984"></a>
  2. #### 2.3.10 搜索及排序切片和数组
  3. 标准库提供了 sort 包来实现常见的搜索和排序操作。您可以使用 sort 包中的函数`func Ints(a []int)` 来实现对 int 类型的切片排序。例如`sort.Ints(arri)`,其中变量 arri 就是需要被升序排序的数组或切片。为了检查某个数组是否已经被排序,可以通过函数`IntsAreSorted(a []int) bool` 来检查,如果返回 true 则表示已经被排序。
  4. 类似的,可以使用函数`func Float64s(a []float64)` 来排序 float64 的元素,或使用函数`func Strings(a []string)` 排序字符串元素。
  5. 想要在数组或切片中搜索一个元素,该数组或切片必须先被排序(因为标准库的搜索算法使用的是二分法)。然后,您就可以使用函数 `func SearchInts(a []int, n int) int` 进行搜索,并返回对应结果的索引值。
  6. 可以通过查看 [官方文档](https://golang.org/pkg/sort/) 来获取更详细的信息。
  7. <a name="d7f75726"></a>
  8. #### 2.3.11 append 函数常见操作
  9. 1. 将切片b的元素追加到切片a之后:`a = append(a, b...)`
  10. 2. 复制切片a的元素到新的切片b上:

b = make([]T, len(a)) copy(b, a)

  1. 3. 删除位于索引i的元素:`a = append(a[:i], a[i+1]...)`
  2. 4. 切除切片a中从索引ij位置的元素:`a = append(a[:i], a[j:]...)`
  3. 5. 为切片a扩展j个元素长度:`a = append(a, make([]T, j)...)`
  4. 6. 在索引i的位置插入元素x`a = append(a[:i], append([]T{x}, a[i:]...)...)`
  5. 7. 在索引 i 的位置插入长度为 j 的新切片:`a = append(a[:i], append(make([]T, j), a[i:]...)...)`
  6. 8. 在索引 i 的位置插入切片 b 的所有元素:`a = append(a[:i], append(b, a[i:]...)...)`
  7. 9. 取出位于切片a最末尾的元素x`x, a = a[len(a)-1], a[:len(a)-1]`
  8. 10. 将元素x追加到切片a`a = append(a, x)`
  9. <a name="13e31e73"></a>
  10. ### 2.4 多维切片
  11. 和数组一样,切片通常也是一维的,但是也可以由一维组合成高维。通过分片的分片(或者切片的数组),长度可以任意动态变化,所以 Go 语言的多维切片可以任意切分。而且,内层的切片必须单独分配(通过 make 函数)。
  12. ---
  13. <a name="2a810f9a"></a>
  14. ## 3. map
  15. map 是一种特殊的数据结构:一种元素对(pair)的无序集合,pair的一个元素是key,对应的另一个元素是value,所以这个结构也称为关联数组或字典。这是一种快速寻找值的理想结构:给定key,对应的value可以迅速定位。
  16. <a name="99419af3"></a>
  17. ### 3.1 声明
  18. map是引用类型,使用如下声明:

var map1 map[keytype] valuetype var map1 map[string] int

  1. 在声明的时候不需要直到map的长度,map是可以动态增长的。
  2. 未初始化的map的值是nil
  3. **key** 可以是任意可以用 == 或者 != 操作符比较的类型,比如stringintfloat。所以数组、切片和结构体不能作为key(含有数组切片的结构体不能作为key,只包含内建类型的struct是可以作为key的),但是指针和接口类型可以。
  4. **value** 可以是任意类型的;通过使用空接口类型,我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言。
  5. **map** 传递给函数的代价很小:在32为机器上占4个字节,64位机器上占8个字节,无论实际上存储了多少数据。通过keymap中寻找值是很快的,比线性查找快得多,但是仍然比从数组和切片的索引中直接读取要慢100倍;所以如果很在乎性能的话还是建议用切片来解决问题。
  6. <a name="fd3910dc"></a>
  7. ### 3.2 初始化
  8. 1. 使用字面量初始化:`map[ktype]vtype{k1:v1, k2:v2, ..., kn:vn}`
  9. 2. 使用字面量初始化空映射:`map[ktype]vtype{}`
  10. 3. 使用make函数初始化:

var map1 = make(map[keytype]valuetype) map1 := make(map[keytype]valuetype)

  1. <br />**不要使用new,永远用make来构造map**<br />**注意** 如果错误的使用new()分配了一个引用对象,你会获得一个空引用的指针,相当于声明了一个未初始化的变量并且取了它的地址。
  2. <a name="10b1c89c"></a>
  3. ### 3.3 操作
  4. <a name="13cff7f1"></a>
  5. #### 3.3.1 获取元素的数量:
  6. `len(map1)`
  7. <a name="420e433f"></a>
  8. #### 3.3.2 获取map1对应key1的值:
  9. `map1[key1]`
  10. <a name="d53e7db6"></a>
  11. #### 3.3.3 设置key1对应的值为val1:
  12. `map1[key1] = val1`
  13. <a name="85075eb2"></a>
  14. #### 3.3.4 key1赋值
  15. 令`v := map1[key1]` 可以将key1对应的值赋值给v;如果map中没有key1存在,那么v将被赋值为map1的值类型的空值。
  16. <a name="c3e47d6b"></a>
  17. #### 3.3.5 测试键值对是否存在:

var1, isPresent = map1[key1]

  1. isPresent返回一个bool值:如果key1存在于map1val1就是key1对应的value值,并且isPresenttrue;如果key1不存在,val1就是一个空值,并且isPresent会返回false
  2. 如果只是想判断某个key是否存在而不关心它对应的值到底是什么:

_, os := map1[key1]

  1. <a name="578982a1"></a>
  2. #### 3.3.6 从map1删除key1:
  3. `delete(map1, key1)`, 如果key1不存在,该操作不会产生错误。
  4. map_testelement.go:

package main import “fmt”

func main() { var value int var isPresent bool

  1. map1 := make(map[string]int)
  2. map1["New Delhi"] = 55
  3. map1["Beijing"] = 20
  4. map1["Washington"] = 25
  5. value, isPresent = map1["Beijing"]
  6. if isPresent {
  7. fmt.Printf("The value of \"Beijing\" in map1 is: %d\n", value)
  8. } else {
  9. fmt.Printf("map1 does not contain Beijing")
  10. }
  11. value, isPresent = map1["Paris"]
  12. fmt.Printf("Is \"Paris\" in map1 ?: %t\n", isPresent)
  13. fmt.Printf("Value is: %d\n", value)
  14. // delete an item:
  15. delete(map1, "Washington")
  16. value, isPresent = map1["Washington"]
  17. if isPresent {
  18. fmt.Printf("The value of \"Washington\" in map1 is: %d\n", value)
  19. } else {
  20. fmt.Println("map1 does not contain Washington")
  21. }

}

  1. 输出结果:

The value of “Beijing” in map1 is: 20 Is “Paris” in map1 ?: false Value is: 0 map1 does not contain Washington

  1. <a name="2a3e399c"></a>
  2. #### 3.3.7 for-rang的配套用法
  3. 可以使用for循环构造map:

for key, value := range map1 { … }

  1. 第一个返回值keymap中的key值,第二个返回值则是该key对应的value值;这两个都是仅for循环内部可见的局部变量。其中第一个返回值key值是一个可选元素。如果你只关心值,可以这么使用:

for _, value := range map1 { … }

  1. 如果只想获取key,可以这么使用:

for key := range map1 { fmt.Printf(“key is: %d\n”, key) }

  1. 示例 maps_forrange.go:

package main import “fmt”

func main() { map1 := make(map[int]float32) map1[1] = 1.0 map1[2] = 2.0 map1[3] = 3.0 map1[4] = 4.0 for key, value := range map1 { fmt.Printf(“key is: %d - value is: %f\n”, key, value) } }

  1. 输出:

key is: 3 - value is: 3.000000 key is: 1 - value is: 1.000000 key is: 4 - value is: 4.000000 key is: 2 - value is: 2.000000

  1. **注意** map不是按照key的顺序排列的,也不是按照value的序排列的。
  2. <a name="93b4c558"></a>
  3. #### 3.3.8 map类型的切片:

var slice1 = []map[keytype]valuetype{} var map1 = make(map[keytype]valuetype)

slice1 = append(slice1, map1)

  1. 示例 maps_forrange2.go:

package main import “fmt”

func main() { // Version A: items := make([]map[int]int, 5) for i:= range items { items[i] = make(map[int]int, 1) items[i][1] = 2 } fmt.Printf(“Version A: Value of items: %v\n”, items)

  1. // Version B: NOT GOOD!
  2. items2 := make([]map[int]int, 5)
  3. for _, item := range items2 {
  4. item = make(map[int]int, 1) // item is only a copy of the slice element.
  5. item[1] = 2 // This 'item' will be lost on the next iteration.
  6. }
  7. fmt.Printf("Version B: Value of items: %v\n", items2)

}

  1. 输出:

Version A: Value of items: [map[1:2] map[1:2] map[1:2] map[1:2] map[1:2]] Version B: Value of items: [map[] map[] map[] map[] map[]]

  1. 需要注意的是,应当像 A 版本那样通过索引使用切片的 map 元素。在 B 版本中获得的项只是 map 值的一个拷贝而已,所以真正的 map 元素没有得到初始化。
  2. 9. map的键值对调<br />这里对调是指调换keyvalue。如果map的值类型可以作为key且所有的value是唯一的:
  3. 示例 invert_map.go:

package main import ( “fmt” )

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() { invMap := make(map[int]string, len(barVal)) for k, v := range barVal { invMap[v] = k } fmt.Println(“inverted:”) for k, v := range invMap { fmt.Printf(“Key: %v, Value: %v / “, k, v) } } ```


参考:

https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md