美文网首页
Go array、slice、map和struct的对比

Go array、slice、map和struct的对比

作者: 小黑胖_ | 来源:发表于2018-12-10 11:15 被阅读0次

这篇讲解go语言中数据存储类型array、slice、map和struct,要清楚它们那些是值传递,那些是指针传递(也就是引用类型),这对后面的数据处理非常重要!

1.数组Array

定义数组的格式:

var <varName> [n]<type>

image.png

示例如下:

package main
import "fmt"
func main() {
    //先声明,后赋值
    var a [2] string
    a[0]="hello"
    a[1]="world"
    fmt.Println(a[0],a[1])
    fmt.Println(a)
    //声明并赋值
    prime:=[6]int{2,3,5,7,11,13}
    fmt.Println(prime)
}

image.png

数组需要注意的点:

  • 1、数组长度也是类型的一部分,因此具有不同长度的数组为不同类型;
  • 2、注意区分指向数组的指针和指针数组;
  • 3、数组在Go中为值类型;(这是和Java非常不同的一点)
  • 4、数组之间可以使用==或者!=进行比较,但不可以使用<>
  • 5、可以使用new来创建数组,此方法返回一个指向数组的指针
  • 6、Go支持多维数组

区分指向数组的指针和指针数组

package main
import "fmt"
func main() {
    //可以利用...不指定数组长度
    a:=[...]int{9:1}
    //指向数组的指针
    var p *[10]int=&a
    fmt.Print(p)
}

image.png
package main
import "fmt"
func main() {
    x,y :=1,2
    //指针数组
    a:=[...]*int{&x,&y}
    fmt.Print(a)
}

image.png

2.切片slice(重点)

切片需要注意的点:

  • 1、其本身并不是数组,它指向底层的数组。(切片为引用类型)
  • 2、作为变长数组的替代方案,可以关联底层数组的局部或全部;
  • 3、可以直接创建或从底层数组获取生成;
  • 4、一般使用make()创建,使用len()获取元素个数,cap()获取容量;
  • 5、如果多个slice指向相同的底层数组,其中一个的值改变会影响全部。

Slice的第一种创建方式:

package main
import "fmt"
func main() {
    //创建一个切片,跟数组相比没有规定长度
    p:=[]int{2,3,5,7,11,13}
    fmt.Println("p==",p)
    for i := 0; i < len(p); i++ {
        fmt.Println(i,p[i])
    }
}

Slice的第二种创建方式:

package main
import "fmt"
func main() {
    //利用make创建包含3个元素,容量为10的slice
    //提前分配容量10是为了增加元素而在此分配内存
    s1:=make([]int,3,10)
    fmt.Println(len(s1),cap(s1))
    //注意打印出来的时候只包含三个元素
    fmt.Println(s1)
}

image.png

Slice与底层数组的对应关系:

image.png

Reslice的理解:

  • 1、Reslice时索引以被slice的切片为准;
  • 2、索引不可以超过被slice的切片的容量cap()值;
  • 3、索引越界不会导致底层数组的重新分配而引发错误。
package main
import "fmt"
func main() {
    a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'}
    //前闭后开
    sa:=a[2:5]
    //转化为字符串
    fmt.Println(string(sa))
    fmt.Println(len(sa),cap(sa))
    //从sa中取出元素--reslice功能
    sb:=sa[3:5]
    //打印出来的是fg,但是sa中并没有fg,这是为什么呢?
    //sa的本质是指针,指向的数组的地址,它会包含从3到最后一个元素的地址。(就像图中阴影部分的元素)
    fmt.Println(string(sb))
}

image.png

Append的理解:

  • 1、可以在slice尾部追加元素;
  • 2、可以将一个slice追加在另一个slice尾部;
  • 3、如果最终长度未超过追加到slice的容量则返回原始slice,如果超过追加到的slice的容量则将重新分配数组并拷贝原始数据。
package main
import "fmt"
func main() {
    s1:=make([]int,3,6)
    //按格式输出
    fmt.Printf("%p\n",s1)
    s1 =append(s1,1,2,3)
    fmt.Printf("%v %p\n",s1,s1)
    s1 =append(s1,1,2,3)
    fmt.Printf("%v %p\n",s1,s1)
}

image.png

3.map

map的基本定义:

  • 1、类似其它语言中哈希表或者字典,以key-value形式存储数据;
  • 2、Key必须是支持==!=比较运算的类型,不可以是函数、mapslice
  • 3、Map查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍;
  • 4、Map使用make()创建,支持:=这种简写方式,make([keyType] valueType,cap)cap表示容量,可省略;超出容量时会自动扩容,但尽量提供一个合理的初始值;使用len()获取元素的个数。
  • 5、键值对不存在时自动添加,使用delete()删除某键值对,比如delete(m,1)
  • 6、使用for rangemapslice进行迭代操作。

map创建的方式一:

package main
import "fmt"
func main() {
    //声明一个map,int是key类型,string是value类型
    var m map[int]string
    //初始化map
    m =map[int]string{}
    fmt.Print(m)
}

map创建的方式二:

package main
import "fmt"
func main() {
    //使用make创建一个map
    m:=make(map[int]string)
    m[1]="OK"
    a:=m[1]
    fmt.Println(m)
    fmt.Print(a)
}

image.png

通过rangemap进行初始化操作如下:

package main
import "fmt"
func main() {
    //创建一个slice
    sm:=make([]map[int]string,5)
    //对一个slice进行迭代,并对slice中的map进行初始化(i对代表的索引下标,v是代表对应的值)
    for i:= range sm {
        //注意是通过索引去改变map中的值,否则只是普通的值拷贝
        sm[i] =make(map[int]string,1)
        sm[i][1] ="OK"
        fmt.Println(sm[i])
    }
    fmt.Println(sm)
}

image.png

使用map作为函数的参数传递:引用传递

package main
import "fmt"
func main() {
    a:=map[string]int{
        "Michaeljian":22,
        "Jerry":20,
    }
    mapTest(a)
    fmt.Print(a["Michaeljian"])
}
//将map作为参数传递进去并修改对应的值
func mapTest(b map[string]int) {
    b["Michaeljian"] = 18
}

image.png

4.struct

struct的知识要点:

  • 1、Go中的struct与C中的struct非常相似,并且Go没有class,使用type<Name>struct{} 定义结构,名称遵循可见性规则;
  • 2、支持指向自身的指针类型成员,支持匿名结构,可用作成员或定义成员变量,匿名结构也可以用于map值;
  • 3、可以使用字面值对结构进行初始化,允许直接通过指针来读写结构成员;
  • 4、相同类型的成员可进行直接拷贝赋值;
  • 5、支持==!=比较运算,但不支持><
  • 6、支持匿名字段,本质上是定义了以某种类型名为名称的字段;
  • 7、可以使用匿名字段指针;
  • 8、嵌入结构作为匿名字段看起来像继承,但不是继承。

struct的定义和赋值:

package main
import "fmt"
type person struct {
    Name string
    Age int
}
func main() {
    a:=person{}
    a.Name ="Michaeljian"
    a.Age =25
    fmt.Print(a)
}

使用struct作为函数传递的参数:值传递

package main
import "fmt"
type person struct {
    Name string
    Age int
}
func main() {
    //字面值初始化
    a:=person{
        Name:"Michaeljian",
        Age:25,
    }
    fmt.Println(a)
    A(a)
    fmt.Println(a)
}
func A(p person) {
    p.Age = 12
    fmt.Println("A",p)
}

image.png

如何想要将struct作为引用传递,需要借助指针:

package main
import "fmt"
type person struct {
    Name string
    Age int
}
func main() {
    //字面值初始化
    a:=person{
        Name:"Michaeljian",
        Age:25,
    }
    /**
    //一般在开发中会采用这种的方式,直接将person的地址赋值给a
        a:= &person{
        Name:"Michaeljian",
        Age:25,
    }
    //此时注意要修改Name的值话直接采用下面的方式即可。
    a.Name="Jerry"
    */
    fmt.Println(a)
    //传入struct的地址
    A(&a)
    fmt.Println(a)
}
func A(p *person) {
    p.Age = 12
    fmt.Println("A",p)
}

image.png

使用匿名结构类型声明struct,格式如下:

package main
import "fmt"
func main() {
  //可以a:=&struct{}{}做地址传递
    a:= struct {
        Name string
        Age int
    }{
        Name:"Michaeljian",
        Age:19,
    }
    fmt.Print(a)
}

使用struct的嵌入结构,格式如下:

package main
import "fmt"
type human struct {
    Sex int
}
type teacher struct {
    human
    Name string
    Age int
}
type student struct {
    human
    Name string
    Age int
}
func main() {
    //如何给嵌入结构human中的字段赋值,human:human{Sex:1}
    a:=teacher{Name:"Michaeljian",Age:25,human:human{Sex:1}}
    b:=student{Name:"Jerry",Age:24,human:human{Sex:2}}
    a.Name ="Jerry"
    a.Sex = 100
    fmt.Print(a,b)
}

image.png

参考资料

https://github.com/Unknwon/go-web-foundation

作者:Michaelhbjian
链接:https://www.jianshu.com/p/3d87e7e6f3c7
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

相关文章

网友评论

      本文标题:Go array、slice、map和struct的对比

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