GO语言的进阶之路-Golang高级数据结构定义

                      GO语言的进阶之路-Golang高级数据结构定义

                                              作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

  我们之前学习过Golang的基本数据类型,字符串和byte,以及rune也有所了解,但是说起高级点的数据类型,可能我们还是不太清楚,那么今天就跟着我脚步一起学习一下这些高级数据类型数据吧。相信有部分人可能学习过Python,那么我这篇博客基本上不用看了,因为对你来说会觉得so easy。因为太多的相似之处了,只是写法不同。本章主要介绍数组(array),切片(scice),字典(map),结构体(struct)等等。

一.数组

  有可能你学习过shell或是python,其实从输出的角度上来说,两者区别不大,但是Golang的数组那是别有一番风味啊,首先在学习数组之前,你要了解数组的两个参数重要参数,一个是数组的长度,一个是数组的容量。只要你明白了golang语言中数组这两个性质,那么在定义的数组时你就会跳过一些坑。比如说你想把容量为3的数组赋值给容量为10的数组,是不可行的,因为容量为三的数组,其长度是3,容量为10的数组,其长度是10,(如果你想把一个数组赋值给另一个数组,首先要让数组的长度相等,其次两边的类型要一致)数组的长度是类型的一部分,所以容量为3的数组是无法赋值给容量为10的数组,就是因为其长度不同,当然你也可以说是类型不同导致。

  最后我要强调的是在Golang定义一个数组后,这个数组的容量是没法改变的。

1.定义一个数组并循环看其初值; 

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     var num [3]int //表示定义一个容量为3的数组,如果没有赋初值的话默认就是"0".
14     fmt.Printf("该数组的第一个数字是:%d
",num[0])
15     fmt.Printf("该数组的最后一个数字是:%d
",num[len(num)-1])
16     for i,v := range num {
17         fmt.Printf("数组的下标是:%d,数组的下标对应的初值是: %d
",i,v)
18     }
19     for _,v := range num {
20         fmt.Printf("数组的初值是:%d
",v)
21     }
22 }
23 
24 
25 
26 #以上代码执行结果如下:
27 该数组的第一个数字是:0
28 该数组的最后一个数字是:0
29 数组的下标是:0,数组的下标对应的初值是: 0
30 数组的下标是:1,数组的下标对应的初值是: 0
31 数组的下标是:2,数组的下标对应的初值是: 0
32 数组的初值是:0
33 数组的初值是:0
34 数组的初值是:0

2.数组的花式定义和赋值

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     var   num [5]int  //先定义一个容量为5的数组num。
14     num = [5]int{1,3,5,7,9} //给这num数组赋值
15     fmt.Printf("num数组的元素是:%d

",num)
16 
17     var   a [3]int = [3]int{1,2,3} //将一个容量为三长度也为3的数组赋值给另一个容量为三的数组.
18     fmt.Printf("a数组的元素是:%d
",a)
19     fmt.Printf("a[1]所对应的值是:%d
",a[1]) //表示取a数组下标对应是1的value.
20     fmt.Printf("a数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员

",cap(a),len(a),(cap(a)-len(a))) //cap函数用于计算数组的容量,len函数用于计算数组的长度.
21 
22 
23     b := [...]int{1,2,3,4} //这种定义方式其实就是不写具体的容量参数,那么容量的值就和长度是相等的。
24     fmt.Printf("b数组的元素是:%d
",b)
25     fmt.Printf("该数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员

",cap(b),len(b),(cap(b)-len(b)))
26 
27     c := [...]int{4:20,7:-1}  //定义下标为4的值为20,下标为7的值为-1。给指定数组下标赋初值,数组的长度为最大下标的加1,如果一个数组没有写明容量的话,会根据其下标最大的元素来定义其容量和长度。
28     fmt.Printf("c数组的元素是:%d
",c)
29     fmt.Printf("该数组的容量是:%d,该容量的长度是:%d,还可以存取%d个成员

",cap(c),len(c),(cap(c)-len(c)))
30 }
31 
32 
33 #以上代码执行结果如下:
34 num数组的元素是:[1 3 5 7 9]
35 
36 a数组的元素是:[1 2 3]
37 a[1]所对应的值是:2
38 a数组的容量是:3,该容量的长度是:3,还可以存取0个成员
39 
40 b数组的元素是:[1 2 3 4]
41 该数组的容量是:4,该容量的长度是:4,还可以存取0个成员
42 
43 c数组的元素是:[0 0 0 0 20 0 0 -1]
44 该数组的容量是:8,该容量的长度是:8,还可以存取0个成员

 3.数组的内存大小以及内存地址的查看;

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import (
11     "fmt"
12     "unsafe"
13 )
14 
15 func main()  {
16     array1 := [4]int{1,2,3} //定义array2这个数组,开辟了一款内存。
17     fmt.Printf("array1的元素是:%d
",array1)
18     fmt.Printf("array1数组所占内存是:%d bytes
",unsafe.Sizeof(array1)) //一个数组占有8个字节,容量为4的数组其内存是就是32字节
19     var  array2 [4]int  //定义一个
20     array2 = array1
21     fmt.Printf("array1的地址是:%d
array2的地址是:%d
",&array1[0],&array2[0])
22 
23     var  n1,n2 int
24     n1 = 100
25     n2 = n1  //定义的n1和n2都是单独的容器,他们的内存地址是不一样的哟!
26     fmt.Printf("n1的内存地址是:%d
n2的内存地址是:%d
",&n1,&n2)  //打印n1和n2的内存地址
27 
28     fmt.Println(n2)
29     fmt.Println(n1 == n2) //这是判断两个变量对应的值是否相同!如果是就为真(true),是否不是九尾假(false)
30 }
31 
32 
33 #以上代码输出结果如下:
34 array1的元素是:[1 2 3 0]
35 array1数组所占内存是:32 bytes
36 array1的地址是:825741296640
37 array2的地址是:825741296768
38 n1的内存地址是:825741271528
39 n2的内存地址是:825741271536
40 100
41 false

4.字节数组

  其实我们在之前就用过关于数组的东西,比如字节数组“[]byte”,其实它就是一个数组

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import (
11     "crypto/md5"
12     "fmt"
13 )
14 
15 func main()  {
16     data := []byte("yinzhengjie")  //定义一个byte数组.
17     md5sum := md5.Sum(data) //调用Golang的md5算法将字节数组换算成一个唯一的md5值用于文件校验。
18     fmt.Printf("%x
",md5sum) //打印其的md5值
19     fmt.Printf("%x
",255) //一个十六进制的数字的取之范围是"00-FF",所以2个16进制表示一个字符。md5就是由十六进制的数字组成的。
20 }
21 
22 
23 
24 #以上代码执行结果如下:
25 a1424987f80af77e96f540ccda1e68e5
26 ff

5.数组的应用

 1 [root@yinzhengjie ~]# more md5.go 
 2 /*
 3 #!/usr/bin/env gorun
 4 @author :yinzhengjie
 5 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 6 EMAIL:y1053419035@qq.com
 7 */
 8 
 9 
10 
11 package main
12 
13 import (
14         "io/ioutil"
15         "fmt"
16         "os"
17         "crypto/md5"
18 )
19 
20 func main() {
21         var s string
22         for i := 1; i < len(os.Args); i++ {
23                 s = os.Args[i]
24                 printFile(s)
25         }
26 }
27 
28 func printFile(name string) {
29         buf, err := ioutil.ReadFile(name) //读取文件的内容传给buf,当然它接受到的数据时仍然是字节,即[]uint8.
30         if err != nil {
31                 fmt.Println(err)
32                 return
33         }
34         md5sum := md5.Sum(buf) //把字节buf的值用md5算法算出其md5值。
35         fmt.Printf("经计算,文件'%v'的MD5值是:%x
",os.Args[1],md5sum)
36 }
37 [root@yinzhengjie ~]# 
38 [root@yinzhengjie ~]# go run md5.go startup.cfg 
39 经计算,文件'startup.cfg'的MD5值是:c577d25cb647991e2b44e12c67649fcc
40 [root@yinzhengjie ~]# 

二.切片

   Golang的切片长得和数组很像,我们可以对一个数组做切片。要注意的是:当我们对一个数组做切片的时候,如果我们修改了切片下标所对应的值,那么被切片的数组的值也会跟着改变,因为他们都指向了同一块内存地址。

1.对数组做切片操作;

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import (
11     "fmt"
12 )
13 
14 func main()  {
15     primes := [8]int{2,3,5,7,9,11,13,15,} //定义一个数组
16     fmt.Printf("`primes`数组的值:%d
",primes)
17     var  sum []int = primes[1:4]   //定义一个切片
18     fmt.Printf("`sum`切片的值:%d
",sum)
19     fmt.Printf("`sum[0]`所对应的内存地址是:%x
",&sum[0])
20     fmt.Printf("`primes[1]`所对应的内存地址是:%x
",&primes[1])
21     var  s1 []int
22     s1 = sum
23     fmt.Printf("`s1`切片对应的值为:%d
",s1)
24     fmt.Printf("s1[0] == sum[0]为:%v
",&s1[0] == &sum[0])
25 }
26 
27 
28 
29 #以上代码输出结果如下:
30 `primes`数组的值:[2 3 5 7 9 11 13 15]
31 `sum`切片的值:[3 5 7]
32 `sum[0]`所对应的内存地址是:c042046088
33 `primes[1]`所对应的内存地址是:c042046088
34 `s1`切片对应的值为:[3 5 7]
35 s1[0] == sum[0]为:true

2.切片的原理;

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     names := [4]string{ //定义了一个字符串数组
14         "尹正杰",
15         "百度",
16         "谷歌",
17         "翻墙",
18     }
19     fmt.Println(names)
20 
21     a := names[0:2]
22     b := names[1:3]
23     fmt.Println(a,b)
24 
25     b[0] = "xxx" //修改b的元素,会将names的对应的地址做相应的修改。
26     fmt.Println(a,b)
27     fmt.Println(names)
28 }
29 
30 
31 #以上代码输出结果如下:
32 [尹正杰 百度 谷歌 翻墙]
33 [尹正杰 百度] [百度 谷歌]
34 [尹正杰 xxx] [xxx 谷歌]
35 [尹正杰 xxx 谷歌 翻墙]

3.切片的字面量

  嗨,可能你有可能听不懂“字面量”,好吧,其实它就是对切片做初始化赋值,仅此而已!

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     num := []int{100,200,300,400,500}   //切片的初始化方法,专业术语叫做切片字面量。
14     fmt.Println(num)
15 
16     r := []bool{true,false,true,true}
17     fmt.Println(r)
18 }
19 
20 
21 
22 #以上代码输出结果如下:
23 [100 200 300 400 500]
24 [true false true true]

4.切片的花式玩法;

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     num := []int{2,3,5,7,9,11,13}  //定义一个切片
14     fmt.Println(num)
15     num = num[1:4] //第一次对切片做切片操作,取值结果为:[3 5 7]
16     fmt.Println(num)
17     num = num[:2] //第二次切了又切,取值结果为[3 5]
18     fmt.Println(num)
19     num = num[1:] //第三次是在第二次切片操作后又一次切片操作,取值结果为[5]
20     fmt.Println(num)
21 }
22 
23 
24 
25 #以上操作结果如下:
26 [2 3 5 7 9 11 13]
27 [3 5 7]
28 [3 5]
29 [5]

5.空切片;

  切片包括2个属性,即长度和容量.因此我们不能看两个切片的长度为0就说这2个变量是相等的。
 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     num := []int{1,2,3}  //定义一个切片
14     var s []int //定义一个空切片
15     fmt.Printf("`s`的值为:%v;长度为:%d;容量为%d
",s,len(s),cap(s))
16     if s == nil {
17         fmt.Printf("s为空
")
18     }
19     s1 := num[:0] //将切片num的值赋值给s1.
20     fmt.Printf("`s1`的值为:%v;长度为:%d;容量为%d
",s1,len(s1),cap(s1))
21     fmt.Println(s1 == nil)  //虽然s1的值为[],长度为0,但是容量为3,因此该切片不是空切片!切片包括2个属性,即长度和容量。
22 }
23 
24 
25 
26 #以上代码输出结果如下:
27 `s`的值为:[];长度为:0;容量为0
28 s为空
29 `s1`的值为:[];长度为:0;容量为3
30 false

6.切片的追加操作

  切片和数组不同,数组没有网容器里添加元素的方法,但是切片可以的。也就是说,只要切片的容量固定,我们可以根据容量大小往里添加数据相应的元素。

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     var s []int //定义一个空切片s.
14     s = []int{1,2,3} //给这个空切片s赋值.
15     slice_attribute(s)
16     s = append(s,0) //往切片s追加一个"0"元素。
17     slice_attribute(s)
18     s = append(s,2,3,4) //继续往切片s追加“2,3,4”等元素。
19     slice_attribute(s)
20 
21 }
22 
23 func slice_attribute(s []int)  {
24     fmt.Printf("len=%d cap=%d %v
",len(s),cap(s),s)  //打印切片的长度,容量以及对应的value.
25 }
26 
27 
28 
29 
30 #以上代码输出结果如下:
31 len=3 cap=3 [1 2 3]
32 len=4 cap=6 [1 2 3 0]
33 len=7 cap=12 [1 2 3 0 2 3 4]

7.用make函数定义一个切片;

  make函数不仅仅可以定义一个切片,还可以定义一个map(你可以理解成字典)。

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     slice1 := make([]int,5)  //表示定义一个长度为5的切片
14     my_slice("slice1",slice1)
15     slice2 := make([]int,0,5) //表示定义一个长度为0,容量为5的切片
16     my_slice("slice2",slice2)
17     slice3 := slice2[:2]
18     my_slice("slice3",slice3)
19     slice4 := slice3[2:5]
20     my_slice("slice4",slice4)
21 }
22 
23 func my_slice(s string ,x []int)  {
24     fmt.Printf("`%s`切片长度为:%d 切片容量为:%d 切片中的元素是:%v
",s,len(x),cap(x),x)
25 }
26 
27 
28 #以上代码执行结果如下:
29 `slice1`切片长度为:5 切片容量为:5 切片中的元素是:[0 0 0 0 0]
30 `slice2`切片长度为:0 切片容量为:5 切片中的元素是:[]
31 `slice3`切片长度为:2 切片容量为:5 切片中的元素是:[0 0]
32 `slice4`切片长度为:3 切片容量为:3 切片中的元素是:[0 0 0]

8.小试牛刀;

  好了,关于切片的基本上这些就够用了,我们可以来小试牛刀一下啦~看看你掌握了多少;

A.反转切片的值;

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import "fmt"
11 
12 func main()  {
13     var   num []int
14     num = []int{1,3,5,7}
15     fmt.Printf("切片反转之前的顺序是:%d
",num)
16     my_rerversal(num)
17     fmt.Printf("切片反转之后的顺序是:%d
",num)
18     }
19 
20 func my_rerversal(s []int) {  //该函数用于反转
21     for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
22         s[i], s[j] = s[j], s[i]
23     }
24 }
25 
26 
27 #以上代码执行结果如下:
28 切片反转之前的顺序是:[1 3 5 7]
29 切片反转之后的顺序是:[7 5 3 1]

B.随机反转切片的值;

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 
 9 package main
10 
11 import (
12     "bufio"
13     "os"
14     "fmt"
15 
16     "strconv"
17 )
18 
19 var   (
20     s string
21     line string
22 )
23 func main()  {
24     f := bufio.NewReader(os.Stdin)
25     num := []int{100,200,300,400,500,600,700,800}
26     fmt.Printf("现有一些数字:·