go学习(二、内建容器)

本文介绍了Go语言中的数组、切片、Map以及字符串的基本概念和操作。数组定义及遍历、切片的创建与扩展、Map的定义与遍历等关键知识点进行了详细讲解。此外,还探讨了Go语言中数组和切片作为函数参数的差异,以及切片如何通过引用修改原数组。最后,展示了字符串的rune类型及其在处理Unicode编码时的优势。
摘要由CSDN通过智能技术生成

2.1 数组

go也是有数组了,接下来我们学习一个数组。

2.1.1 定义数组

go定义数组有几种方式:

func main() {
    // go数组的定义
    var arr1 [3]bool    // 没有赋初值 但是go默认会赋初值
    arr2 := [3]int{1, 2, 3}    // 这是又初值
    arr3 := [...]int{1,2,3,4,5}    // 这个是根据后面定义个数来确定数组个数

    var arr4 [4][5]int   // 二维数组,4行5列

    fmt.Println(arr1, arr2, arr3, arr4)

    return
}

go的数组,也跟别的数组不一样,是个数在前面,后面加类型,比较容易跟别的语言搞混。

go也是支持二维数组的,二维数组第一个是多少行,后面是多少列

[false false false] [1 2 3] [1 2 3 4 5] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]

2.1.2 数组遍历

接下来我们看看数组的遍历,

  • 常规方式
for i:=0; i<len(arr2); i++ {
        fmt.Println(arr2[i])
}

这种根据长度的遍历方式是我们常规的操作。

  • range
for i, v := range arr3 {
        fmt.Println(i, v)
}

go提供了一个关键字,range,就是遍历各个容器的值的,不仅是数组,还有后面的切片和map。而且range的返回值还是有点意思,返回两个值,第一个是下标,第二个才是数组的值。

2.1.3 数组作为函数参数

那我们来看看数组作为函数参数的时候,是怎么传参的:

  • 值传递
func funcarr1(arr [3]int) {    // [3]int 是一个类型,不存在跟c一样的只传首地址
    arr[0] = 100
}

func main() {
    // go数组的定义
    var arr1 [3]bool    
    arr2 := [3]int{1, 2, 3}
    arr3 := [...]int{1,2,3,4,5}

    var arr4 [4][5]int   // 二维数组,4行5列

    fmt.Println(arr1, arr2, arr3, arr4)

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

    funcarr1(arr2)

    for i, v := range arr2 {
        fmt.Println(i, v)
    }

    return
}

根据打印出来的结果,明显这个数组就是传值,这个跟我们c/c++是一样的,如果这样传就是直接传值。

那如果这个数组很大,那拷贝是不是很难,所以是存在用数组指针的:

  • 指针传递
func funcarr2(arr *[3]int) {
    arr[0] = 100
}

这个打印的结果明显是修改了,所以go也是可以传数组的。

但是go会用数组么,其实在go语言中,一般是不用数组的,只会用切片。

2.2 切片

go的大名鼎鼎的切片,其实我学了切片,感觉有点像ArrayList,但是还是不同,不过可以借助ArrayList来帮助我们学习切片也是有意义的。

2.2.1 切片的引入

arr5 := [...]int{0,1,2,3,4,5,6,7,8,9}

    fmt.Println("s := [2:6]", arr5[2:6])
    fmt.Println("s := [:6]", arr5[:6])
    fmt.Println("s := [2:]", arr5[2:])
    fmt.Println("s := [:]", arr5[:])

定义一个arr5的数组,然后可以用[]进行各种切片,这个名字起的真是有意思。

打印出来的结果:

s := [2:6] [2 3 4 5]
s := [:6] [0 1 2 3 4 5]
s := [2:] [2 3 4 5 6 7 8 9]
s := [:] [0 1 2 3 4 5 6 7 8 9]

跟我们思路的一样,是感觉下标的切片。

2.2.2 切片做为函数参数

上面我们是写了数组做为函数参数,因为数组的类型是需要

func funcarr3(arr []int) {        // 不用限制类型
    arr[0] = 100
}

func main() {

    arr5 := [...]int{0,1,2,3,4,5,6,7,8,9}

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

    funcarr3(s)

    fmt.Println(s)


    return
}

切片传过去的时候就不需要大小,并且切片作为函数参数,传的是引用,所以就是函数内部可以修改数组中的值。

[2 3 4 5]
[100 3 4 5]

本来刚刚想用数组作为参数,能不能给切片传值,但是发现还是不行,go语言的类型限制的很死。

2.2.3 切片的扩展

老师出了一道比较难的题:

arr6 := [...]int{0,1,2,3,4,5,6,7}

s1 := arr6[2:6]
s2 := s1[3:5]

fmt.Println(s1, s2)

s1和s2的值,直接打印出来:

[2 3 4 5] [5 6]

这样我们就看到s2是可以取到值的,为啥会这样,其实这个切片是有一个len,和一个cap的,也就是这个切片底层实现是有一个最大容量cap,还有就是一个我们有效数据len,我们可以修改代码,打印出来看看:

arr6 := [...]int{0,1,2,3,4,5,6,7}

    s1 := arr6[2:6]
    s2 := s1[3:5]

    fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
    fmt.Printf("s2=%v len(s2)=%d cap(s2)=%d\n", s2, len(s2), cap(s2))

打印结果:

s1=[2 3 4 5] len(s1)=4 cap(s1)=6
s2=[5 6] len(s2)=2 cap(s2)=3

这样是不是很像是一个ArrayList,其实是有一个容量的,也有一个有效长度,所以切片在给数据做切片的时候,是保留了向后扩展的能力,这样s2才能取出值。

如果使用append添加元素的时候,如果超过原来数组的cap,就会从新分配一个新更大的底层数组,然后把值拷贝过去,原来的数组如果没有人使用,就由go垃圾回收掉。

2.2.4 直接定义切片

我们一般在使用操作中,应该不会先定义一个数组,然后在改成切片的,我们是可以直接定义切片的:

var s3 []int // 没有值,值是nil
fmt.Println(s3)
s3 = append(s3, 1)

s4 := []int{3,4,5}
fmt.Println(s4)

// 知道长度,需要定义一个切片
s5 := make([]int, 10)   
fmt.Println(s5)

// 需要定义cap也是可以的
s6 := make([]int, 10, 20)   
fmt.Println(s6)

直接定义的切片有这么4种方式,不过第3种方式我们会经常用,这样看看是不是更像ArrayList了。

2.2.5 切片操作

通过菜鸟教程,我们知道除了append()外还有copy()函数。

其他函数目前我也不知道,哈哈哈哈。

2.3 map

go也是又map的,接下来学习学习map。

2.3.1 定义

还是直接来学习一个map的定义:

m := map[string]string {
        "name" : "aaa",
        "course" : "golang",
        "site" : "immoc",
    }
    fmt.Println(m)

    mm := map[string]map[string]string {
        "name" : m,
    }
    fmt.Println(mm)


m2 := make(map[string]int)

m3 := make(map[string]int, 10)
fmt.Printf("m2=%v len(m2)=%d\n", m2, len(m2))
fmt.Printf("m3=%v len(m3)=%d\n", m3, len(m3))

上面是map定义的方式,map可以直接定义,也可以使用make,不过使用make是不能制定大小的,跟切片不一样,map是插入的时候有值了,自然就有值了。

2.3.2 遍历

map遍历应该到这里了,都知道了,是的,map遍历就使用range。

for k, v := range m {
	fmt.Println(k, v)
}

这里就不介绍了。

2.3.3 操作

我们尝试一下删除操作:

m["name"]            // 获取值
delete(m, "name")    // 删除

这个也没啥难度的

2.3.4 map的key

  • map使用哈希表,key必须可以比较相等

  • 除了slice,map,function的内建类型都可以作为key

  • Struct类型不包含上述字段,有可以作为key

2.3.5 示例

寻找最长不含有重复字符的字串:

(这个之后在写,哈哈哈)

2.4 字符和字符串

2.4.1 rune

rune相等与其他语言的char,因为需要兼容Unicode编码,所以类型是int32,这样中文也可以支持。

ss := "hello world 你好,世界"
for i, c := range []rune(ss) {
	fmt.Printf("(i=%d c=%c) ", i, c)
}

运行结果:

(i=0 c=h) (i=1 c=e) (i=2 c=l) (i=3 c=l) (i=4 c=o) (i=5 c= ) (i=6 c=w) (i=7 c=o) (i=8 c=r) (i=9 c=l) (i=10 c=d) (i=11 c= ) (i=12 c=) (i=13 c=) (i=14 c=) (i=15 c=) (i=16 c=)

go语言就可以直接打印出中文,如果跟其他语言一样,强转成[]byte也是可以的。

2.4.2 字符串操作

  • 使用range遍历pos,rune对

  • 使用utf8.RuneCountInString获取字符数量(支持中文)

  • 使用len获取字节长度(按字节)

  • 使用[]byte获得字节

2.4.3 其他字符串操作

  • Fields,Split,Join

  • Contains,Index

  • ToLower,ToUpper

  • Trim,TrimRight,TrimLeft

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值