go语言-golang基础-数据类型数组,数组切片,映射

7.7 数组

数组是Go语言编程中最常用的数据结构之一。顾名思义,数组就是指一系列同一类型数据的集合。数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数组的长度。

以下为一些常规的数组声明方法:

[32]byte                    // 长度为32的数组,每个元素为一个字节 
[2*N] struct { x, y int32 } // 复杂类型数组
[1000]*float64              // 指针数组
[3][5]int                   // 二维数组
[2][2][2]float64            // 等同于[2]([2]([2]float64))

从以上类型也可以看出,数组可以是多维的,比如[3][5]int就表达了一个3行5列的二维整型数组,总共可以存放15个整型元素。
在Go语言中,数组长度在定义后就不可更改,在声明时长度可以为一个常量或者一个常量表达式(常量表达式是指在编译期即可计算结果的表达式)。
数组的长度是该数组类型的一个内置常量,可以用Go语言的内置函数len()来获取。
下面是一个获取数组arr元素个数的写法:

arrLength := len(arr)

1) 元素访问

  • 索引。用数组下标来访问数组中的元素。

数组下标从0开始,len(array)-1 则表示最后一个元素的下标。下面的示例遍历整型数组并逐个打印元素内容:

for i := 0; i < len(array); i++ { 
    fmt.Println("Element", i, "of array is", array[i])
}
  • 用range遍历容器中的元素。

示例如下:

for i, v := range array {
    fmt.Println("Array element[", i, "]=", v)
 }

2)值类型
需要特别注意的是,在Go语言中数组是一个值类型(value type)。所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。如果将数组作为函数的参数类型,则在函数调用时该参数将发生数据复制。因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是所传入数组的一个副本。
下面用例子来说明这一特点:

package main
import "fmt"
func modify(array [10]int) {
    array[0] = 10             // 试图修改数组的第一个元素 
    fmt.Println("In modify(), array values:", array)
}
func main() {
    array := [5]int{1,2,3,4,5}     // 定义并初始化一个数组
    modify(array)                 // 传递给一个函数,并试图在函数体内修改这个数组内容
    fmt.Println("In main(), array values:", array)
}

/*
该程序的执行结果为:
    In modify(), array values: [10 2 3 4 5]
    In main(), array values: [1 2 3 4 5]
*/

从执行结果可以看出,函数modify()内操作的那个数组跟main()中传入的数组是两个不同的实 例。那么,如何才能在函数内操作外部的数据结构呢?我们将在2.3.6节中详细介绍如何用数组切 片功能来达成这个目标。

例子:

package main

import (
    "fmt"
)

func main() {
    var names [3]string
    var signIns [3]bool
    var scores [3]float64

    // 类型
    fmt.Printf("1: %T
", names)
    fmt.Printf("2: %T
", signIns)
    fmt.Printf("3: %T
", scores)

    // 零值
    fmt.Printf("4: %#v
", names)
    fmt.Printf("5: %#v
", signIns)
    fmt.Printf("6: %#v
", scores)

    // 字面量
    // 第一种赋值方式
    names = [3]string{"01-Jordan", "02-kobe", "03-kd"}
    // names = [1]string{"02-kobe} //

    fmt.Printf("7: %#v
", names)

    // 第二种赋值方式
    names = [...]string{"04-curry", "05-Steven", "06-James"}
    fmt.Printf("8: %#v
", names)

    testnames := [...]string{"07-牛", "08-猪"}
    fmt.Printf("9: %T
", testnames)

    //第三种赋值方式
    names = [3]string{1: "kk"}
    fmt.Printf("10: %#v
", [3]string{1: "kk"})

    //操作
    //关系运算符 == !=
    fmt.Println("11: ", names == [3]string{})
    fmt.Println("12: ", names == [3]string{1: "kk"})

    //元素 访问&修改 索引(0,1,2,3。。n-1)
    fmt.Printf("13: %q
", names[0])
    names[0] = "09-tt"
    fmt.Printf("14: %#v
", names)

    //函数len()
    fmt.Println("15: ", len(names))

    //遍历
    //方式一:
    for i := 0; i < len(names); i++ {
        fmt.Println("16: ", i, names[i])
    }

    //方式二:
    for i, v := range names {
        fmt.Println("17: ", i, v)
    }

    //定义一个数组,每个元素也是数组
    //二维数组
    d1 := [3][2]int{}
    d2 := [3][2]int{2: [2]int{5, 2}, 1: [2]int{4, 1}, 0: [2]int{7, 0}} //有3个元素数组,每个元素是有2个元素的数组
    //[2]int = {0,0}
    //{[2]int,[2]int,[2]int}
    //{{0,0},{0,0},{0,0}}
    fmt.Printf("18: %#v
", d1)
    fmt.Printf("19: %#v
", d2)

}

/*
$ go run array.go
1: [3]string
2: [3]bool
3: [3]float64
4: [3]string{"", "", ""}
5: [3]bool{false, false, false}
6: [3]float64{0, 0, 0}
7: [3]string{"01-Jordan", "02-kobe", "03-kd"}
8: [3]string{"04-curry", "05-Steven", "06-James"}
9: [2]string
10: [3]string{"", "kk", ""}
11:  false
12:  true
13: ""
14: [3]string{"09-tt", "kk", ""}
15:  3
16:  0 09-tt
16:  1 kk
16:  2
17:  0 09-tt
17:  1 kk
17:  2
18: [3][2]int{[2]int{0, 0}, [2]int{0, 0}, [2]int{0, 0}}
19: [3][2]int{[2]int{7, 0}, [2]int{4, 1}, [2]int{5, 2}}
*/

7.8 数组切片

相同数据类型组成的一组长度可变的序列。
在前一节里我们已经提过数组的特点:数组的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。显然这种数据结构无法完全满足开发者的真实需求。
不用失望,Go语言提供了数组切片(slice)这个非常酷的功能来弥补数组的不足。
初看起来,数组切片就像一个指向数组的指针,实际上它拥有自己的数据结构,而不仅仅是个指针。数组切片的数据结构可以抽象为以下3个变量:

  • 一个指向原生数组的指针;
  • 数组切片中的元素个数;
  • 数组切片已分配的存储空间。

从底层实现的角度来看,数组切片实际上仍然使用数组来管理元素,基于数组,数组切片添加了一系列管理功能,可以随时动态扩充存放空间,并且可以被随意传递而不会导致所管理的元素被重复复制。

1)创建数组切片
创建数组切片的方法主要有两种——基于数组和直接创建,下面我们来简要介绍一下这两种方法。

  • 基于数组

数组切片可以基于一个已存在的数组创建。数组切片可以只使用数组的一部分元素或者整个数组来创建,甚至可以创建一个比所基于的数组还要大的数组切片。下面代码基 于一个数组的前5个元素创建一个数组切片。

// slice.go 
package main
import "fmt"
func main() {
    // 先定义一个数组
    var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 基于数组创建一个数组切片
    var mySlice []int = myArray[:5]
    fmt.Println("Elements of myArray: ") 
    for _, v := range myArray {
        fmt.Print(v, " ")
    }
    fmt.Println("
Elements of mySlice: ")

    for _, v := range mySlice { 
        fmt.Print(v, " ")
    }
    fmt.Println()
}
/*
运行结果为:
Elements of myArray: 
1 2 3 4 5 6 7 8 9 10 
Elements of mySlice: 
12345
*/

Go语言支持用myArray[first:last]这样的方式来基于数组生成一个数组切片,而且这个用法还很灵活,比如下面几种都是合法的。

// 基于myArray的所有元素创建数组切片: 
mySlice = myArray[:]
// 基于myArray的前5个元素创建数组切片:
mySlice = myArray[:5]
// 基于从第5个元素开始的所有元素创建数组切片:
mySlice = myArray[5:]
  • 直接创建

并非一定要事先准备一个数组才能创建数组切片。Go语言提供的内置函数make()可以用于灵活地创建数组切片。下面的例子示范了直接创建数组切片的各种方法。

// 创建一个初始元素个数为5的数组切片,元素初始值为0:
mySlice1 := make([]int, 5)
// 创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10) 
// 直接创建并初始化包含5个元素的数组切片:
mySlice3 := []int{1, 2, 3, 4, 5} 

当然,事实上还会有一个匿名数组被创建出来,只是不需要我们来操心而已。

2) 元素遍历
操作数组元素的所有方法都适用于数组切片,比如数组切片也可以按下标读写元素,用len()函数获取元素个数,并支持使用range关键字来快速遍历所有元素。
传统的元素遍历方法如下:

for i := 0; i <len(mySlice); i++ { 
    fmt.Println("mySlice[", i, "] =", mySlice[i])
} 

使用range关键字可以让遍历代码显得更整洁。range表达式有两个返回值,第一个是索引,第二个是元素的值:

for i, v := range mySlice { 
    fmt.Println("mySlice[", i, "] =", v)
} 

对比上面的两个方法,我们可以很容易地看出使用range的代码更简单易懂。

3)动态增减元素
可动态增减元素是数组切片比数组更为强大的功能。与数组相比,数组切片多了一个存储能 力(capacity)的概念,即元素个数和分配的空间可以是两个不同的值。合理地设置存储能力的值,可以大幅降低数组切片内部重新分配内存和搬送内存块的频率,从而大大提高程序性能。

假如你明确知道当前创建的数组切片最多可能需要存储的元素个数为50,那么如果你设置的存储能力小于50,比如20,那么在元素超过20时,底层将会发生至少一次这样的动作——重新分配一块“够大”的内存,并且需要把内容从原来的内存块复制到新分配的内存块,这会产生比较明显的开销。

给“够大”这两个字加上引号的原因是系统并不知道多大才是够大,所以只是一个简单的猜测。

比如,将原有的内存空间扩大两倍,但两倍并不一定够,所以之前提到的内存重新 分配和内容复制的过程很有可能发生多次,从而明显降低系统的整体性能。但如果你知道最大是50并且一开始就设置存储能力为50,那么之后就不会发生这样非常耗费CPU的动作,从而达到空间换时间的效果。

数组切片支持Go语言内置的cap()函数和len()函数,下面代码简单示范了这两个内置函数的用法。
可以看出,cap()函数返回的是数组切片分配的空间大小,而len()函数返回的是数组切片中当前所存储的元素个数。

// slice2.go 
package main
import "fmt" func main() {
    mySlice := make([]int, 5, 10)
    fmt.Println("len(mySlice):", len(mySlice))
    fmt.Println("cap(mySlice):", cap(mySlice)) 
}
/*
该程序的输出结果为:
    len(mySlice): 5
    cap(mySlice): 10
*/

如果需要往上例中mySlice已包含的5个元素后面继续新增元素,可以使用append()函数。下面的代码可以从尾端给mySlice加上3个元素,从而生成一个新的数组切片:

mySlice = append(mySlice, 1, 2, 3) 

函数append()的第二个参数其实是一个不定参数,我们可以按自己需求添加若干个元素,甚至直接将一个数组切片追加到另一个数组切片的末尾:

mySlice2 := []int{8, 9, 10}
// 给mySlice后面添加另一个数组切片
mySlice = append(mySlice, mySlice2...)

需要注意的是,我们在第二个参数mySlice2后面加了三个点,即一个省略号,如果没有这个省略号的话,会有编译错误,因为按append()的语义,从第二个参数起的所有参数都是待附加的元素。因为mySlice中的元素类型为int,所以直接传递mySlice2是行不通的。加上省略号相当于把mySlice2包含的所有元素打散后传入。
上述调用等同于:

mySlice = append(mySlice, 8, 9, 10)

数组切片会自动处理存储空间不足的问题。如果追加的内容长度超过当前已分配的存储空间(即cap()调用返回的信息),数组切片会自动分配一块足够大的内存。

4)基于数组切片创建数组切片
类似于数组切片可以基于一个数组创建,数组切片也可以基于另一个数组切片创建。下面的例子基于一个已有数组切片创建新数组切片:

oldSlice := []int{1, 2, 3, 4, 5}
newSlice := oldSlice[:3] // 基于oldSlice的前3个元素构建新数组切片

有意思的是,选择的oldSlicef元素范围甚至可以超过所包含的元素个数,比如newSlice可以基于oldSlice的前6个元素创建,虽然oldSlice只包含5个元素。只要这个选择的范围不超 过oldSlice存储能力(即cap()返回的值),那么这个创建程序就是合法的。newSlice中超出 oldSlice元素的部分都会填上0。

5)内容复制
数组切片支持Go语言的另一个内置函数copy(),用于将内容从一个数组切片复制到另一个数组切片。如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。下面的示例展示了copy()函数的行为:

slice1 := []int{1, 2, 3, 4, 5} 
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中 
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置

例子:

// slice.go
package main import
"fmt" /* 切片的三个元素 *array lenght cap() */ func main() { var names []string // 类型 fmt.Printf("1: %T ", names) // 零值 fmt.Printf("2: %#v ", names) //nil,没有进行初始化赋值 // 初始化 // 字面量 // 第一种 names = []string{} //空切片,已经初始化,但是元素数量为0 fmt.Printf("3: %#v", names) names = []string{"01-猪", "02-牛", "03-羊"} fmt.Printf("4: %#v ", names) //第二种 names = []string{1: "01-狗", 20: ""} fmt.Printf("5: %#v ", names) //第三种 make函数
   arrSlice := make([]type,len,cap)
names = make([]string, 5) fmt.Printf("6: %#v ", names) names = make([]string, 1, 10) //元素容量总数为10,1为申请的容量,初始化的元素数量,不能超过10 fmt.Printf("7: %#v ", names) names = make([]string, 3, 10) names[0] = "a" names[1] = "b" names[2] = "c" //names[3] = "d" fmt.Println("8: ", names[0]) fmt.Println("9: ", names[1]) fmt.Println("10: ", names[2]) //fmt.Println(names[3]) //获取长度和容量 fmt.Println("11: ", len(names), cap(names)) //添加元素 names = append(names, "d") fmt.Printf("12: %#v ", names) fmt.Println("13: ", len(names), cap(names)) //遍历 for i := 0; i < len(names); i++ { fmt.Println("14: ", i, names[i]) } for i, v := range names { fmt.Println("15: ", i, v) } //copy切片之间的赋值 //前后元素相等 aSlice := []string{"a", "b", "c"} bSlice := []string{"d", "e", "f"} copy(aSlice, bSlice) fmt.Println("16: ", aSlice, bSlice) //后面元素多于前面 aSlice1 := []string{"a", "b", "c"} bSlice1 := []string{"d", "e", "f", "g"} copy(aSlice1, bSlice1) fmt.Println("17: ", aSlice1, bSlice1) //后面元素少于前面 aSlice2 := []string{"a", "b", "c"} bSlice2 := []string{"d", "e"} //前面是目的,后面是源 copy(aSlice2, bSlice2) fmt.Println("18: ", aSlice2, bSlice2) //切片操作 =>数组,切片 => 新生成一个切片 nums := []int{0, 1, 2, 3, 4, 5, 6} // nums = make([]int,6,10) // len =6 , cap =10 numsChildren := nums[3:4] //start <= end <=len() new_cap = cap-start fmt.Println(nums[1:3]) fmt.Printf("19: %T,%#v ", numsChildren, numsChildren) numsChildren = append(numsChildren, 100) fmt.Printf("20: %#v,%#v ", nums, numsChildren) fmt.Println("21: ", cap(numsChildren)) // start <= end <= max <= cap new_cap= max-start nums = []int{3, 4, 5, 76, 8, 9, 0} numsChildren = nums[3:4:4] fmt.Println("22: ", cap(numsChildren)) numsChildren = append(numsChildren, 100) fmt.Println("23: ", cap(numsChildren)) fmt.Printf("24: %#v,%#v ", nums, numsChildren) numsArray := [6]int{0, 1, 2, 3, 5, 6} //start <= end <= len new_cap = len -start arrayChildren := numsArray[3:4] fmt.Printf("25: %T,%#v ", arrayChildren, arrayChildren) fmt.Println("26: ", cap(arrayChildren)) arrayChildren = append(arrayChildren, 100) fmt.Printf("27: %#v,%#v ", arrayChildren, numsArray) numsArray = [6]int{1, 2, 3, 4, 5, 6} // start <= end <= len new_cap = len-start arrayChildren = numsArray[3:4:4] fmt.Printf("28: %T,%#v ", arrayChildren, arrayChildren) fmt.Println("29: ", cap(arrayChildren)) arrayChildren = append(arrayChildren, 100) fmt.Printf("30: %#v,%#v ", arrayChildren, numsArray) } /* $ go run slice.go 1: []string 2: []string(nil) 3: []string{}
4: []string{"01-猪", "02-牛", "03-羊"} 5: []string{"", "01-狗", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "鸡"} 6: []string{"", "", "", "", ""} 7: []string{""} 8: a 9: b 10: c 11: 3 10 12: []string{"a", "b", "c", "d"} 13: 4 10 14: 0 a 14: 1 b 14: 2 c 14: 3 d 15: 0 a 15: 1 b 15: 2 c 15: 3 d 16: [d e f] [d e f] 17: [d e f] [d e f g] 18: [d e c] [d e] [1 2] 19: []int,[]int{3} 20: []int{0, 1, 2, 3, 100, 5, 6},[]int{3, 100} 21: 4 22: 1 23: 2 24: []int{3, 4, 5, 76, 8, 9, 0},[]int{76, 100} 25: []int,[]int{3} 26: 3 27: []int{3, 100},[6]int{0, 1, 2, 3, 100, 6} 28: []int,[]int{4} 29: 1 30: []int{4, 100},[6]int{1, 2, 3, 4, 5, 6}
*/
// nilSlice.go
package main

import "fmt"

func main() {
    var nilSlice []int       //定义一个nil值的切片
    var emptySlice = []int{} //emptySlice := []int{}

    fmt.Printf("1: %T,%#v
", nilSlice, nilSlice)
    fmt.Printf("2: %T,%#v
", emptySlice, emptySlice)

    nilSlice = append(nilSlice, 1)
    emptySlice = append(emptySlice, 2)
    fmt.Printf("3: %T,%#v
", nilSlice, nilSlice)
    fmt.Printf("4: %T,%#v
", emptySlice, emptySlice)

}

/*
$ go run nilslice.go
1: []int,[]int(nil)
2: []int,[]int{}
3: []int,[]int{1}
4: []int,[]int{2}
*/
// mutilslice.go
package main

import "fmt"

func main() {
    multi := [][]string{}
    fmt.Printf("1: %T,%#v
", multi, multi)

    // 增加元素
    multi = append(multi, []string{"1", "2", "3"})
    multi = append(multi, []string{"3", "4", "5", "6"})
    fmt.Println("2: ", multi)

    fmt.Printf("3: %T,%#v
", multi[0], multi[0])
    fmt.Printf("4: %T,%#v
", multi[0][1], multi[0][1])

    // 修改元素
    multi[0][1] = "xyz"

    // 增加第二个切片组中的元素
    multi[1] = append(multi[1], "cccc")
    fmt.Println("5: ", multi)

}


/*
$ go run mutilslice.go
1: [][]string,[][]string{}
2:  [[1 2 3] [3 4 5 6]]
3: []string,[]string{"1", "2", "3"}
4: string,"2"
5:  [[1 xyz 3] [3 4 5 6 cccc]]

 */
// sliceoperator.go
package main

import "fmt"

func main() {
    //移除切片操作
    //第一个元素或最后一个元素
    nums := []int{1, 2, 3, 4, 5, 6}
    // nums[start:end]
    // start =0 start 省略
    // end= len(nums) end省略
    //移除第一个元素
    nums = nums[1:]
    fmt.Println("1: ", nums)
    //移除最后一个元素
    nums = nums[:len(nums)-1]
    fmt.Println("2: ", nums)
    //移除中间的元素
    //[2,3,4,5]移除3
    //切片操作,copy

    copy(nums[1:], nums[2:])
    nums = nums[:len(nums)-1]
    fmt.Println("3: ", nums)
}
/*
$ go run sliceoperator.go
1:  [2 3 4 5 6]
2:  [2 3 4 5]
3:  [2 4 5]
*/

7.9 map(映射)

在C++/Java中,map一般都以库的方式提供,比如在C++中是STL的std::map<>,在C#中是Dictionary<>,在Java中是Hashmap<>,在这些语言中,如果要使用map,事先要引用相应的库。而在Go中,使用map不需要引入任何库,并且用起来也更加方便。
map是一堆键值对的未排序集合。比如以身份证号作为唯一键来标识一个人的信息,则这个map可以定义下面代码的方式。

//map1.go 
package main
import "fmt"
// PersonInfo是一个包含个人详细信息的类型 
type PersonInfo struct {
    ID string
    Name string 
    Address string
}
func main() {
var personDB map[string] PersonInfo
    personDB = make(map[string] PersonInfo)
    
    // 往这个map里插入几条数据
    personDB["12345"] = PersonInfo{"12345", "Tom", "Room 203,..."} 
    personDB["1"] = PersonInfo{"1", "Jack", "Room 101,..."}
    
    // 从这个map查找键为"1234"的信息 
    person, ok := personDB["1234"]

    // ok是一个返回的bool型,返回true表示找到了对应的数据 
    if ok {
            fmt.Println("Found person", person.Name, "with ID 1234.")
    } else {
        fmt.Println("Did not find person with ID 1234.")
    } 
}

上面这个简单的例子基本上已经覆盖了map的主要用法,下面对其中的关键点进行细述。

1) 变量声明
map的声明基本上没有多余的元素,比如:

var myMap map[string] PersonInfo

其中,myMap是声明的map变量名,string是键的类型,PersonInfo则是其中所存放的值类型。
2) 创建
我们可以使用Go语言内置的函数make()来创建一个新map。下面的这个例子创建了一个键类型为string、值类型为PersonInfo的map:

myMap = make(map[string] PersonInfo)

也可以选择是否在创建时指定该map的初始存储能力,下面的例子创建了一个初始存储能力为100的map:

myMap = make(map[string] PersonInfo, 100)

关于存储能力的说明,可以参见2.3.6节中的内容。 创建并初始化map的代码如下:

myMap = map[string] PersonInfo{
    "1234": PersonInfo{"1", "Jack", "Room 101,..."},
}

3) 元素赋值
赋值过程非常简单明了,就是将键和值用下面的方式对应起来即可:

myMap["1234"] = PersonInfo{"1", "Jack", "Room 101,..."}

4) 元素删除
Go语言提供了一个内置函数delete(),用于删除容器内的元素。下面我们简单介绍一下如何用delete()函数删除map内的元素:

delete(myMap, "1234") 

上面的代码将从myMap中删除键为“1234”的键值对。如果“1234”这个键不存在,那么这个调用将什么都不发生,也不会有什么副作用。但是如果传入的map变量的值是nil,该调用将导致 程序抛出异常(panic)。

5) 元素查找 在Go语言中,map的查找功能设计得比较精巧。而在其他语言中,我们要判断能否获取到一个值不是件容易的事情。判断能否从map中获取一个值的常规做法是:

  • 声明并初始化一个变量为空;
  • 试图从map中获取相应键的值到该变量中;
  • 判断该变量是否依旧为空,如果为空则表示map中没有包含该变量。

这种用法比较啰唆,而且判断变量是否为空这条语句并不能真正表意(是否成功取到对应的值),从而影响代码的可读性和可维护性。有些库甚至会设计为因为一个键不存在而抛出异常,让开发者用起来胆战心惊,不得不一层层嵌套try-catch语句,这更是不人性化的设计。
在Go语言中,要从map中查找一个特定的键,可以通过下面的代码来实现:

value, ok := myMap["1234"] 
if ok {     // 找到了
    // 处理找到的value 
}

判断是否成功找到特定的键,不需要检查取到的值是否为nil,只需查看第二个返回值ok,这让表意清晰很多。配合:=操作符,让你的代码没有多余成分,看起来非常清晰易懂。

例子1:

//map.go
package main

import "fmt"

func main() {
    // 每个同学的成绩
    // key = ID value = 成绩
    var scores map[string]float64
    fmt.Printf("1: %T,%#v,%#v
", scores, scores, scores == nil)

    //初始化
    //字面量
    scores = map[string]float64{} //空的map
    fmt.Printf("2: %T,,%#v,%#v
", scores, scores, scores == nil)

    scores = map[string]float64{"kk": 80, "Jordan": 98, "Kobe": 100}
    fmt.Printf("3: %T,%#v
", scores, scores)

    //make
    scores = make(map[string]float64) //==map[string]float64{}
    fmt.Printf("4: %T,%#v
", scores, scores)

    scores = map[string]float64{"kk": 80, "Jordan": 98, "Kobe": 100}
    fmt.Println("5: ", len(scores))

    // key = value
    // 查找
    fmt.Println("6: ", scores["kk"])
    fmt.Println("7: ", scores["Jordan"])
    fmt.Println("8: ", scores["Kobe"])
    //判断key是否存在
    v, ok := scores["yy"] //key不存在的情况
    fmt.Println("9: ", v, ok)

    v, ok = scores["Jordan"] //key存在的情况
    fmt.Println("10: ", v, ok)

    //
    scores["kk"] = 89
    fmt.Println("11: ", scores)

    //增加
    scores["kd"] = 95
    fmt.Println("12: ", scores)

    //删除映射中有数据的
    delete(scores, "kk")
    fmt.Println("13: ", scores)

    //删除映射中没有的数据
    delete(scores, "curry")
    fmt.Println("14: ", scores)

    //遍历映射
    for k, v := range scores {
        fmt.Println("15: ", k, v)
    }
    for i := range scores {
        fmt.Println("16: ", i, scores[i])
    }
}

/*
$ go run map.go
1: map[string]float64,map[string]float64(nil),true
2: map[string]float64,,map[string]float64{},false
3: map[string]float64,map[string]float64{"Jordan":98, "Kobe":100, "kk":80}
4: map[string]float64,map[string]float64{}
5:  3
6:  80
7:  98
8:  100
9:  0 false
10:  98 true
11:  map[Jordan:98 Kobe:100 kk:89]
12:  map[Jordan:98 Kobe:100 kd:95 kk:89]
13:  map[Jordan:98 Kobe:100 kd:95]
14:  map[Jordan:98 Kobe:100 kd:95]
15:  Jordan 98
15:  Kobe 100
15:  kd 95
16:  Jordan 98
16:  Kobe 100
16:  kd 95

*/

例子2:

// nilmap.go
package main

import "fmt"

func main() {
    var nilMap map[string]string
    fmt.Println("1: ", len(nilMap))
    fmt.Println("2: ", nilMap["Jordan"])

    nilMap["Kobe"] = "basketball Gold"
    fmt.Println("3: ", nilMap)
}
/*
$ go run nilmap.go
1:  0
2:
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /Users/jordan/GolandProjects/LearnGoProject/go-new-course/day02-20200411/nilmap.go:10 +0x1a6
exit status 2

*/

8 sort 排序

队列queue

package main

import "fmt"

func main() {
    //队列
    //先进先出
    queue := []string{}
    //push
    //append
    queue = append(queue, "a", "b")
    queue = append(queue, "c")
    //pop
    x := queue[0]
    queue = queue[1:]
    fmt.Println("1: ", x)

    x = queue[0]
    queue = queue[1:]
    fmt.Println("2: ", x)

    x = queue[0]
    queue = queue[1:]
    fmt.Println("3: ", x)

}

/*
$ go run queue.go
1:  a
2:  b
3:  c
*/
原文地址:https://www.cnblogs.com/malukang/p/12702326.html