美文网首页
go基础编程day4切片slice与map

go基础编程day4切片slice与map

作者: james_chang | 来源:发表于2018-06-21 13:12 被阅读0次

    切片slice

    • 本身并不是数组,它指向底层的数组

    • 作为变长数组的替代方案,可以关联底层数组的局部或者全部

    • 为引用类型

    • 可以直接创建或从底层数组获取生成

    • 使用len()获取元素个数,cap()获取当前容量

    • 一般使用make()创建

    • 如果多个slice指向相同底层数组,其中一个slice的值的改变会影响全部

    • make([]type, len, cap)

    • 其中cap可以省略,则和len的值相同

    • len表示存数的元素个数,cap表示容量

    package main
    
    import (
        "fmt"
    )
    
    // 创建slice
    func main() {
        // 声明空slice
        var s1 []int
        fmt.Println(s1) // []
        // 切片生成slice
        a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
        s2 := a[5:]
        fmt.Println(s2, len(s2), cap(s2)) // [6 7 8 9 0] 5 5
        // make生成slice
        s3 := make([]int, 3, 10)
        fmt.Println(s3, len(s3), cap(s3)) // [0 0 0] 3 10
    }
    

    reslice

    • replace时索引以被slice的切片为准
    • 索引不可以超过被slice的切片的容量cap()的值(注意这里是容量,并不是长度,即便取长度外的值,只要不超过容量,依然可以取到值)
    • 索引越界不会导致底层数组的重新分配而是引发错误

    如果是切片生成的slice则要注意几点:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        // 切片生成slice
        a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
        s2 := a[3:5]
        fmt.Println(s2, len(s2), cap(s2)) // [4 5] 2 7
        // 对长度为2的s2进行切片取长度外的值依然能取到
        s3 := s2[3:5]
        fmt.Println(s3) // [7 8]
    }
    

    这里要明确的是s2取到的slice虽然长度只有2但是他的最大容量是到原数组尾部的,并且,slice是引用的一个地址,所以能取到后面的值

    append

    • 可以在slice尾部追加元素
    • 可以将一个slice追加到另一个slice尾部
    • 如果最终长度未超过追加到slice的容量则返回原始slice
    • 如果超过追加到的slice的容量则将重新分配数组并拷贝原始数据
    package main
    
    import (
        "fmt"
    )
    
    func main() {
        s1 := make([]int, 3, 6)
        fmt.Printf("%p\n", s1) // 0xc420084030
        s1 = append(s1, 1, 2, 3)
        fmt.Printf("%p\n", s1) // 0xc420084030
        s1 = append(s1, 1, 2, 3)
        // 第三次内存地址发生改变,超出原有容量
        fmt.Printf("%p\n", s1) // 0xc42007a060
    }
    
    package main
    
    import (
        "fmt"
    )
    
    func main() {
        a := []int{1, 2, 3, 4, 5}
        s1 := a[2:5]
        s2 := a[1:3]
        fmt.Println(s1, s2)
        s1[0] = 9
        // 改变了s1,s1,s2和a都改变了
        fmt.Println(s1, s2, a)
    }
    

    copy

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        s1 := make([]int, 5, 10)
        s2 := []int{7, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1}
        // 第一个参数是copy到的目标,第二个是被copy的
        // 这里是将s2 copy到 s1
        copy(s1, s2)
        fmt.Println(s1) // [7 8 9 4 5 6]
    }
    
    /*
    总结
    copy最后的长度为copy目标的长度(非容量)
    copy到对象的长度能容纳多少就会容纳多少
    如果被copy的对象长度较小,按照顺序copy,剩下的原封不动
    */
    
    

    map(类似字典)

    • 类似其他语言中的哈希表或者字典,以k_v形式存储数据

    • key必须是支持==或者!=比较运算的类型,不可以是函数、map或slice

    • map查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍

    • map使用make()创建,支持:=这种简写方式

    • make([keytype]valuetype, cap),cap表示容量,可以省略

    • 超出容量时会自动扩容,但尽量供一个合理的初始值

    • 使用len()获取元素个数

    • 键值对不存在时自动添加,使用delete()删除某个键值对
    • 使用for range对map和slice进行迭代操作
    package main
    
    import (
        "fmt"
    )
    
    func main() {
        // 第一种方式
        var m map[int]string
        m = map[int]string{}
        fmt.Println(m)
        // 第二种方式
        var m1 map[int]string = make(map[int]string)
        fmt.Println(m1)
        // 第三种其实就是去掉第二种中多余的部分
        var m2 = make(map[int]string)
        fmt.Println(m2)
        // 简洁方式
        m3 := make(map[int]string)
        fmt.Println(m3)
    }
    

    增删

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        m := make(map[int]string)
        m[1] = "ok"
        a:=m[1]
        b:= m[2]
        fmt.Println(m) // map[1:ok]
        fmt.Println(a) // ok
        fmt.Println(b) // 此处取出的为空
        delete(m,1)
        fmt.Println(m[1]) // 删除了
    }
    

    复杂map

    package main
    
    import "fmt"
    
    func main() {
        m := make(map[int]map[int]string)
        // 如果map内层还有map,内层的map也要初始化
            // 如果不初始化则取出的为空字符,并且赋值的时候报错
        m[1] = make(map[int]string)
        m[1][1] = "ok"
        a := m[1][1]
        fmt.Println(a) // ok
    }
    

    如何知道内层map是否已经初始化了(防止赋值的时候报错)

    package main
    
    import "fmt"
    
    func main() {
        m := make(map[int]map[int]string)
        a := m[1][1]
        fmt.Println(a) // 此时输出为空字符串
        b, ok := m[1][1]
        fmt.Println(b, ok) // 空字符串和false
    }
    // 这个时候就可以加个判断,如果第二个返回值为false就说明没有初始化,否则已经初始化了
    

    对slice和map进行迭代操作

    对元素为map的slice进行迭代的时候,获取到的v是值的copy,所以不能真正修改slice中map的值这个时候迭代k通过k来直接修改map

    // 迭代一个内容为map的slice
    func main() {
        // 5个map
        sm := make([]map[int]string, 5)
        // 想真正修改被迭代对象的值,需要迭代索引,利用索引直接修改迭代对象
        for i := range sm {
            sm[i] = make(map[int]string, 1)
            sm[i][1] = "ok"
            fmt.Println(sm[i])
        }
        fmt.Println(sm)
    
    
        /*
        map[1:ok]
        map[1:ok]
        map[1:ok]
        map[1:ok]
        map[1:ok]
        [map[1:ok] map[1:ok] map[1:ok] map[1:ok] map[1:ok]]
        
        */
    }
    

    map是无序的,但是可以通过key进行间接排序:排序k,通过有序的k来取map中的v

    package main
    
    import (
        "fmt"
        "sort"
    )
    
    func main() {
        m := map[int]string{1: "a", 2: "b", 3: "c", 4: "d", 5: "e"}
        s := make([]int, len(m))
        i := 0
        // 将map中的k存在slice中
        for k := range m {
            s[i] = k
            i++
        }
        // 排序slice
        sort.Ints(s)
        fmt.Println(s)
        // 完成了对k的排序之后就可以有序取map中的值
    }
    

    map k-v调换位置

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        m1 := map[int]string{1: "a", 2: "b", 3: "c", 4: "d", 5: "e"}
        m2 := make(map[string]int)
        fmt.Println(m1)
        for k,v := range m1{
            m2[v] = k
        }
        fmt.Println(m2)
    }
    

    相关文章

      网友评论

          本文标题:go基础编程day4切片slice与map

          本文链接:https://www.haomeiwen.com/subject/gxmqyftx.html