数组

数组

是一同一种数据类型元素的集合。在go语言中,数组从声明时就可以修改数组成员,但是数组大小不变化

数组是值类型而不是引用类型

因此数组支持"==","!="操作符,因为内存总是被初始化过的

数组的基本使用

package main

import "fmt"

func main() {
    var a [3]bool //数组声明
    //数组如果不初始化,默认元素是零,布尔值是false,字符串为空""
    //初始化方式1
    a = [3]bool{true, true, true}
    fmt.Println(a)
    //初始化方式2
    a10 := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8} //也可以这样写 a10 := [9]int{0, 1, 2, 3, 4, 5, 6, 7, 8}
    fmt.Println(a10)
    //初始化方式3
    a3 := [5]int{0: 1, 4: 2}
    fmt.Println(a3) //[1 0 0 0 2]

    //数组的遍历
    citys := [...]string{"北京", "上海", "深圳"}
    for i := 0; i < len(citys); i++ {
        fmt.Println(citys[i]) //逐行输出 北京 上海 深圳
    }
    for i, v := range citys {
        fmt.Println(i, v)
    }
    //多维数组
    //[1,2][3,4][5,6]
    var all [3][2]int
    all = [3][2]int{
        [2]int{1, 2},
        [2]int{3, 4},
        [2]int{5, 6},
    }
    fmt.Println(all)
    fmt.Println("~~~~~~~~~~~~~~~~~~") //多维数组的遍历
    for _, v1 := range all {
        fmt.Println(v1)
        for _, v2 := range v1 {
            fmt.Println(v2)
        }
    }

}

切片

数组的长度是固定的,并且数组长度属于类型一部分,所以数组有很多局限性

切片是一个拥有相同类型元素的可变程度序列.他是基于数组做的一层封装,它非常灵活,支持自动扩容.切片是一个引用类型,它的内部结构包含,地址,长度,和容量.切片一半用于快速地操作一块数据集合

切片是引用数据类型,都指向了底层的数组

package main

import "fmt"

func main() {
    var s1 []int        //定义存放int类型元素的切片
    var s2 []string     //定义存放string类型的切片
    fmt.Println(s1, s2) //[] []
    //初始化
    s1 = []int{1, 2, 3}
    s1 = []int{1, 2, 3, 4}
    s2 = []string{"qwe", "asd", "zxc", "123"}
    fmt.Println(len(s1)) //长度3
    //由数组拿到切片
    a1 := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
    s3 := a1[:4]    //基于一个元素切割,左包含右不包含
    fmt.Println(s3) //[3 4]
    //也有如下写法 c:=a[1:] 1到最后一位 c:=a[:4] 第0位到4 c:=a[:] 全部
    //切片的容量是底层数组从第一个元素到最后的容量
}

本质

切片的本质就是一个框,框住了一块连续的内存.(go语言比较底层,所以只能保存相同的类型值,不像php,python)

切片属于引用类型,真正的数据都是保存在数组里的

用make函数创造切片

package main

import "fmt"

func main() {
    //make函数创造切片,make是申请像操作系统开辟一块内存空间的
    //当容量省略的时候默认容量是和长度一样的
    s1 := make([]int, 5, 10)
    s1 = nil
    fmt.Println("值", s1, "长度", len(s1), "容量", cap(s1)) //值 [0 0 0 0 0] 长度 5 容量 10
}

概念

切片类型不能直接使用==操作符来进行比较

一个nill值的切片并没有底层数组,一个nil值的切片的长度和容量都是0

切片赋值的时候赋值的是内存地址

append

package main

import "fmt"

func main() {
    s1 := []string{"北京", "上海", "深圳", "宁波"}
    //调用append函数,必须用原来的切片变量接收返回值
    s1 = append(s1, "广州", "大")
    //append追加元素,原来的底层数组放不下的时候,就会把底层数组换一个
    //新的数组
    //首先判断,如果新申请的容量大于最终容量,也就是旧容量的俩倍,最终容量就是新申请的容量
    //否则判断,如果切片长度小于1024,则最终容量是就容量的俩倍
    //否则判断,如果切片长度大于等于1024,则最终容量从旧容量开始循环增加原来的四分之一,直到最终容量大于新申请的容量
    //如果最终容量计算值一处,则最终容量就是新申请的容量
    //需要注意的是,切片扩容还会根据切片元素类型的不同而做不同的处理,比如int和string的处理方式就是不一样的
    fmt.Println(s1, cap(s1)) //[北京 上海 深圳 宁波 广州 大] 8
    s1=append(s1,ss...)//表示拆开,类似js中的扩散
}

copy

package main

import "fmt"

func main() {
    //copy函数就是将第一个元素的所有值拷到第二个,直接辅助是赋值的地址
    a1 := []int{1, 2, 3}
    a2 := a1
    var a3 = make([]int, 3, 3) //要先声明空间
    copy(a3, a1)
    a1[2] = 6
    fmt.Println(a1, a2, a3) //[1 2 6] [1 2 6] [1 2 3]
    //删除将a1中索引为1的元素2删除
    a1 = append(a1[:1], a1[2:]...)
    fmt.Println(a1)
    fmt.Println(cap(a1))
}

测试

package main

import "fmt"

func main() {
    // x1 := [...]int{1, 3, 5}
    // s1 := x1[:]
    // fmt.Println(s1, len(s1), cap(s1)) //[1 3 5] 3 3
    // s1 = append(s1[:1], 6, 2)         //
    // fmt.Println(s1, len(s1), cap(s1)) //[1 6] 2 3
    // fmt.Println(x1)                   //[1 6 5]
    a := []int{1, 2, 3}
    b := a
    a = append(a, 7)
    fmt.Println(a, b) //[1 2 3 7] [1 2 3]
}

指针(和c语言类似)

package main

import "fmt"

func main() {
    //取地址
    n := 18
    p := &n
    fmt.Println(*p)        //18
    fmt.Println(p)         //内存地址 0xc000064058
    fmt.Printf("%T\n", p)  //*int
    fmt.Printf("%T\n", *p) //int
    n = 20
    fmt.Println(*p) //20
}
package main

import "fmt"

func main() {
    //var a *int //定义一个int型的内存地址 定义的时候为空指针
    //*a = 100//这样写会报错
    //fmt.Println(*a)
    //正确的写法为下
    var a2 = new(int) //new函数申请一个内存地址
    fmt.Println(a2)//0xc000064058
    fmt.Println(*a2)//0
    *a2 = 100
    fmt.Println(*a2)

}

make也是用于分配额内存的,区别于slice,map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。

new很少用,一般给基本数据类型申请内存,string、int

make是用啦igeislice,map,chan申请内存的,make函数返回的是对应这三个类型的本身

Map

map是一种无序的基于key-value的数据结构,go语言中的map是引用数据类型,必须初始化之后才能使用

package main

import "fmt"

func main() {
    var m1 map[string]int         //默认值为nil,还没有在内存中开辟空间
    m1 = make(map[string]int, 10) //要估算好,map的容量,避免在程序运行期间再动态扩容
    m1["理想"] = 18
    m1["酷炫"] = 35
    fmt.Println(m1)        //map[理想:18 酷炫:35]
    fmt.Println(m1["123"]) //0
    value, ok := m1["qwq"]
    println(ok, value)     //false 0 直接复制是0
    for k, v := range m1 { //遍历
        println(k, v)
    }
    for k := range m1 { //只遍历key
        println(k)
    }
    for _, v := range m1 {
        println(v) //只遍历value
    }
    //删除
    delete(m1, "理想")
    fmt.Println(m1) //map[酷炫:35] 如果删除一个不存在的就没有操作
}
package main

import "fmt"

func main() {
    //元素为切片类型的map
    var s1 = make([]map[int]string, 10, 10)
    s1[0] = make(map[int]string, 1)
    s1[0][10] = "a"
    fmt.Println(s1) //[map[10:a] map[] map[] map[] map[] map[] map[] map[] map[] map[]]
    //值为切片类型的map
    var m1 = make(map[string][]int, 10)
    m1["北京"] = []int{10, 20, 30}
    fmt.Println(m1) //map[北京:[10 20 30]]
}
Last modification:April 22, 2022
如果觉得我的文章对你有用,请随意赞赏