映射-学习Go语言

阅读笔记

maps.go

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m map[string]Vertex

func main() {
    m = make(map[string]Vertex)
    m["Bell Labs"] = Vertex{
        40.68433, -74.39967,
    }
    fmt.Println(m["Bell Labs"])
}

笔记

make 函数会返回给定类型的映射,并将其初始化备用。
1. m就是映射了,将key 映射到value。
2. 内部还是用hash实现的吗?
3. 开始有点类似PHP的数组味道了。

map-literals.go

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m = map[string]Vertex{
    "Bell Labs": Vertex{
        40.68433, -74.39967,
    },
    "Google": Vertex{
        37.42202, -122.08408,
    },
}

func main() {
    fmt.Println(m)
}

笔记

  1. 可以理解,初始化一个hash数组。
  2. key为字符串,value是Vertex 结构体

map-literals-continued.go

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m = map[string]Vertex{
    "Bell Labs": {40.68433, -74.39967},
    "Google":    {37.42202, -122.08408},
}

func main() {
    fmt.Println(m)
}

笔记

  1. 忽略类名的写法

mutaing-maps.go

package main

import "fmt"

func main() {
    m := make(map[string]int)

    m["Answer"] = 42
    fmt.Println("The value:", m["Answer"])

    m["Answer"] = 48
    fmt.Println("The value:", m["Answer"])

    delete(m, "Answer")
    fmt.Println("The value:", m["Answer"])

    v, ok := m["Answer"]
    fmt.Println("The value:", v, "Present?", ok)
}

笔记

m[key] = elem
elem = m[key]
delete(m, key)

elem, ok = m[key]

  1. 数组的操作方式

参考链接

  1. 映射
  2. 映射的文法
  3. 映射的文法 续
  4. 修改映射

切片-学习Go语言

阅读笔记

slices.go

package main

import "fmt"

func main() {
    primes := [6]int{2, 3, 5, 7, 11, 13}

    var s []int = primes[1:4]
    fmt.Println(s)
}

笔记

切片的内部实现,类似一个链表+头尾指针。

数组的切片范围为low<= x < high

slices-pointers.go

package main

import "fmt"

func main() {
    names := [4]string{
        "John",
        "Paul",
        "George",
        "Ringo",
    }
    fmt.Println(names)

    a := names[0:2]
    b := names[1:3]
    fmt.Println(a, b)

    b[0] = "XXX"
    fmt.Println(a, b)
    fmt.Println(names)
}

笔记

切片的数据被引用的。修改切片的数据会直接关联到原来的数组。

slice-literals.go

package main

import "fmt"

func main() {
    q := []int{2, 3, 5, 7, 11, 13}
    fmt.Println(q)

    r := []bool{true, false, true, true, false, true}
    fmt.Println(r)

    s := []struct {
        i int
        b bool
    }{
        {2, true},
        {3, false},
        {5, true},
        {7, true},
        {11, false},
        {13, true},
    }
    fmt.Println(s)
}

笔记

  1. 创建长度为6的int数组,构建一个引用该数组的切片。
  2. 创建长度为6的布尔数组,构建一个引用该数组的切片。
  3. 创建长度为5的结构体数组,构建一个引用该数组的切片。

slice-bounds.go

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}

    s = s[1:4]
    fmt.Println(s)

    s = s[:2]
    fmt.Println(s)

    s = s[1:]
    fmt.Println(s)
}

笔记

  1. 切片的默认值为0到数组长度。
  2. 以数组var a [10]int为例
  3. 切片等价a[0:10] = a[:10] = a[0:] = a[:]

slice-len-cap.go

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Slice the slice to give it zero length.
    s = s[:0]
    printSlice(s)

    // Extend its length.
    s = s[:4]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

笔记

  1. 如何理解切片的长度和容量。按照正常逻辑不是只有长度就够了吗?为啥还要新增一个容量呢?
  2. 切片的长度就是它所包含的元素个数。
  3. 切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
  4. 注意看第二个例子,长度变为0 ,但是容量为什么还是6呢?
  5. 注意第四个例子,是不是起始值变了,才会去修改引用数组的容量?

nil-slices.go

package main

import "fmt"

func main() {
    var s []int
    fmt.Println(s, len(s), cap(s))
    if s == nil {
        fmt.Println("nil!")
    }
}

笔记

  1. 切片的零值是 nil
  2. nil 切片的长度和容量为 0 且没有底层数组。

making-slices.go

package main

import "fmt"

func main() {
    a := make([]int, 5)
    printSlice("a", a)

    b := make([]int, 0, 5)
    printSlice("b", b)

    c := b[:2]
    printSlice("c", c)

    d := c[2:5]
    printSlice("d", d)
}

func printSlice(s string, x []int) {
    fmt.Printf("%s len=%d cap=%d %v\n",
        s, len(x), cap(x), x)
}

笔记

  1. 默认创建len 和cap 相同的切片
  2. a := make([]int, 5) // len(a)=5 cap(a)=5
  3. 注意第四个例子,cap变成了3
  4. 假如d1 := c[3:5],那么cap会变成2
  5. 切片的最大值不能超过原始数组的长度,不然会报错。

slices-of-slice.go

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Create a tic-tac-toe board.
    board := [][]string{
        []string{"_", "_", "_"},
        []string{"_", "_", "_"},
        []string{"_", "_", "_"},
    }

    // The players take turns.
    board[0][0] = "X"
    board[2][2] = "O"
    board[1][2] = "X"
    board[1][0] = "O"
    board[0][2] = "X"

    for i := 0; i < len(board); i++ {
        fmt.Printf("%s\n", strings.Join(board[i], " "))
    }
}

笔记

  1. 这尼玛不是就二维数组了吗?
  2. 创建了一个二维数组的切片,也就是切片的切片

append.go

package main

import "fmt"

func main() {
    var s []int
    printSlice(s)

    // append works on nil slices.
    s = append(s, 0)
    printSlice(s)

    // The slice grows as needed.
    s = append(s, 1)
    printSlice(s)

    // We can add more than one element at a time.
    s = append(s, 2, 3, 4)
    printSlice(s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

笔记

  1. func append(s []T, vs …T) []T 函数原型

  2. 注意第三次追加,cap自动扩充到2^ 3 = 8

  3. 那如果再超过当前的cap呢

  4. 比如 s = append(s, 5,6,7,8),cap会自动扩容到2^4 = 16

  5. len=9 cap=16 [0 1 2 3 4 5 6 7 8]
    
  6. 这难道底层是采用hash+链表实现的吗?还有自动扩容这一说?

  7. Go 切片:用法和本质

range.go

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }
}

笔记

  1. 这不就是PHP数组的遍历,键值对吗?

rang-continued.go

package main

import "fmt"

func main() {
    pow := make([]int, 10)
    for i := range pow {
        pow[i] = 1 << uint(i) // == 2**i
    }
    for _, value := range pow {
        fmt.Printf("%d\n", value)
    }
    for value := range pow {
        fmt.Printf("%d\n", value)
    }
    for key, _ := range pow {
        fmt.Printf("%d\n", key)
    }
}

笔记

  1. 输出键值对的方法。

思考总结

操作数组切片的方法。
1. make
2. append

参考链接

  1. 切片
  2. 切片就像数组的引用
  3. 切片文法
  4. 切片的默认行为
  5. 切片的长度与容量
  6. nil 切片
  7. 用 make 创建切片
  8. 切片的切片
  9. 向切片追加元素
  10. Range
  11. Range续

结构体-学习Go语言

阅读笔记

pointers.go

package main

import "fmt"

func main() {
    i, j := 42, 2701

    p := &i         // point to i
    fmt.Println(*p) // read i through the pointer
    *p = 21         // set i through the pointer
    fmt.Println(i)  // see the new value of i

    p = &j         // point to j
    *p = *p / 37   // divide j through the pointer
    fmt.Println(j) // see the new value of j
}

笔记

Go 拥有指针。指针保存了值的内存地址。

类型 *T 是指向 T 类型值的指针。其零值为 nil。
& 操作符会生成一个指向其操作数的指针。
这也就是通常所说的“间接引用”或“重定向”。

structs.go

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    fmt.Println(Vertex{1, 2})
}

笔记

  1. 这个不是c语言的结构体是一样的吗?

struct-fields.go

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    v.X = 4
    fmt.Println(v)
}

笔记

  1. 内部实现是否类似c语言的指针?

struct-pointers.go

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    p := &v
    p.X = 1e9
    fmt.Println(v)
}

笔记

  1. v 和 p 都是指向结构体的指针吧
  2. p使用隐式间接引用

struct-literals.go

package main

import "fmt"

type Vertex struct {
    X, Y int
}

var (
    v1 = Vertex{1, 2}  // has type Vertex
    v2 = Vertex{X: 1}  // Y:0 is implicit
    v3 = Vertex{}      // X:0 and Y:0
    p  = &Vertex{1, 2} // has type *Vertex
)

func main() {
    fmt.Println(v1, p, v2, v3)
}

笔记

  1. 结构体会初始化字段的默认值
  2. 特殊的前缀 & 返回一个指向结构体的指针。

参考链接

  1. 指针
  2. 结构体
  3. 结构体字段
  4. 结构体指针
  5. 结构体文法