分析go中的类型比较


本文摘自php中文网,作者藏色散人,侵删。

下面由golang教程栏目给大家介绍分析go中的类型比较,希望对需要的朋友有所帮助!

概述

在最近的面试中被面试官问到go之间的类型比较,回答的并不是非常好,根本上来说还是基础不够牢固啊!看了网上的一堆资料,自己做了一些简单的总结,哈哈!

go中的类型

首先来看看go包含的最基础的集中类型

  • 基本类型:go中最基本类型包括整型(int、uint、int8、uint8、int16、uint16、int32、uint32、int64、uint64、byte、rune等)、浮点型(float32、float64)、字符串(string也是个[]rune数组)和比较不常用的复数类型(complex64/complex128)。
  • 复合类型:主要包括结构体数组
  • 引用类型:Slice、Map、Channel、指针
  • 接口类型:Error、io.Reader等

go作为强类型语言并不会和PHP等高级语言自动帮我们进行类型转换,所以我们在比较时必须用==两边的类型必须一致,即使他们底部类型一致也不行。看下面的代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package main

import "fmt"

type A struct {

    Id int

}

type B struct {

    Id int

}

func main() {

    var a int

    var b int16

    // 编译报错:invalid operation a == b (mismatched types int and int16)

    fmt.Println(a == b)

    aStruct := A{Id:5}

    bStruct := B{Id:5}

    // 编译报错:invalid operation: aStruct == bStruct (mismatched types A and B)

    fmt.Println(aStruct == bStruct)

}

所以go不会帮我们做隐式转换,即使底层的类型一致,也不能比较。
接下来我们从不同的类型来分析是否可以进行比较。

基本类型

go的基本类型就比较简单,只要类型是一样的,那么他们就是可以比较的,举个栗子:

1

2

3

4

5

6

7

8

package main

import "fmt"

func main() {

    var a int = 0

    var b int = 1

    // 输出false

    fmt.Println(a == b)

}

不过基本类型中也要注意浮点型的比较就不像我们现实中的一样,比如0.1+0.2在计算中运行结果就不是0.3了,而是0.30000000000000004了

1

2

3

4

5

6

7

8

9

package main

import "fmt"

 

func main() {

    var a float64=0.1

    var b float64=0.2

    // 0.30000000000000004

    fmt.Println(a+b)

}

为什么会这样,可以看下draveness(https://github.com/draveness) 大佬的这篇文章https://draveness.me/whys-the...

复合类型

数组

面试中也经常会问到go数组和切片的区别。数组在go中是必须先确定长度的,也就是长度不能再去扩容。并且它是个值拷贝,做参数传到一个函数中被修改,那么外部的值还是一样的不变的。Slice则相反。那么数组是否可以比较呢,看下面的例子:

1

2

3

4

5

6

7

8

9

10

11

package main

import "fmt"

func main() {

    a := [2]int{1, 2}

    b := [2]int{1, 2}

    c := [2]int{1, 3}

    d := [3]int{1, 2, 4}

    fmt.Println(a == b) // true

    fmt.Println(a == c) // false

    fmt.Println(a == d) // invalid operation: a == d (mismatched types [2]int and [3]int)

}

可以看出,相同长度的数组是可以比较的,而不同长度的数组是不能进行比较的。原因是什么呢?这是因为数组类型中,数组的长度也是类型的一部分,不同长度的数组那么他们的类型也就被认为不同的,所以无法比较。

结构体

同样的Struct也是一样的。Struct的比较也从内部类型开始比较,每一类型的值相等才是相等的。如下例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

package main

import "fmt"

type A struct {

    id int

    name string

}

func main() {

    a := A{id:5,name:"123"}

    b := A{id:5,name:"123"}

    c := A{id:5,name:"1234"}

    fmt.Println(a == b) // true

    fmt.Println(a == c) // false

}

那么可以理解成Struct结构体是可以比较的吗。我们再来看个例子:

1

2

3

4

5

6

7

8

9

10

11

12

package main

import "fmt"

type A struct {

    id int

    name string

    son []int

}

func main() {

    a := A{id:5,name:"123",son:[]int{1,2,3}}

    b := A{id:5,name:"123",son:[]int{1,2,3}}

    fmt.Println(a == b) // invalid operation: a == b (struct containing []int cannot be compared)

}

怎么又变成不可比较的呢?这就要看下面的引用类型了。

引用类型

上面中的例子结构体中带上切片就无法比较了,在go中SliceMap被定义成不能比较的类型。我们来看

如果Slice是可比较,那么用什么来定义是一样的切片呢?如果用地址,那么如果两个地址指向的Slice是一样的呢?这显然不合适。如果和数组一样的方式,那么我切片扩容了呢,就不相等了。所以长度和容量导致不好比较。虽然可以在语言层面解决这个问题,但是 golang 团队认为不值得为此耗费精力。所以Slice被当成不可比较。
同样的Map也被定义成不可比较类型。那么引用类型都是不可比较吗?也不是,看个例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package main

import "fmt"

type A struct {

    id int

    name string

}

func main() {

    a := &A { a : 1, b : "test1" }

    b := &A { a : 1, b : "test1" }

    c := a

    fmt.Println(a == b) // false

    fmt.Println(a == c) // true

    ch1 := make(chan int, 1)

    ch2 := make(chan int, 1)

    ch3 := ch1

    fmt.Println(ch1 == ch2) // false

    fmt.Println(ch1 == ch3) // true

}

引用类型变量存储的是某个变量的内存地址。所以引用类型变量的比较,判断的是这两个引用类型存储的是不是同一个变量。

  • 如果是同一个变量,则内存地址肯定也一样,则引用类型变量相等,用"=="判断为true
  • 如果不是同一个变量,则内存地址肯定不一样,"=="结果为false

接口类型

Go 语言根据接口类型是否包含一组方法将接口类型分成了两类:

  • 使用 runtime.iface结构体表示包含方法的接口
  • 使用 runtime.eface结构体表示不包含任何方法的 interface{} 类型

1

2

3

4

5

6

7

8

9

type eface struct { // 16 字节

    _type *_type

    data  unsafe.Pointer

}

 

type iface struct { // 16 字节

    tab  *itab

    data unsafe.Pointer

}

所以我们可以得知,一个接口值是由两个部分组成的,即该接口对应的类型和接口对应具体的值。接口值的比较涉及这两部分的比较,只有当类型和值都相等(动态值使用==比较),两个接口值才是相等的。看个例子:

1

2

3

4

5

6

7

var a interface{} = 0

var b interface{} = 2

var c interface{} = 0

var d interface{} = 0.0

fmt.Println(a == b) // false

fmt.Println(a == c) // true

fmt.Println(a == d) // false

ac类型相同(都是int),值也相同(都是0,基本类型比较),故两者相等。 ab类型相同,值不等,故两者不等。 ad类型不同,aintdfloat64,故两者不等。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

type A struct {

    a int

    b string

}

 

var a interface{} = A { a: 1, b: "test" }

var b interface{} = A { a: 1, b: "test" }

var c interface{} = A { a: 2, b: "test" }

 

fmt.Println(a == b) // true

fmt.Println(a == c) // false

 

var d interface{} = &A { a: 1, b: "test" }

var e interface{} = &A { a: 1, b: "test" }

fmt.Println(d == e) // false

ab类型相同(都是A),值也相同(结构体A),故两者相等。 ac类型相同,值不同,故两者不等。 de类型相同(都是*A),值使用指针(引用)类型的比较,由于不是指向同一个地址,故不等。

不过需要注意的是,如果接口中类型是切片或者Map不可比较的类型,那么会直接报错的。看个例子:

1

2

3

4

var a interface{} = []int{1, 2}

var b interface{} = []int{1, 2}

// panic: runtime error: comparing uncomparable type []int

fmt.Println(a == b)

ab的类型是切片类型,而切片类型不可比较,所以a == bpanic

接口值的比较不要求接口类型(注意不是动态类型)完全相同,只要一个接口可以转化为另一个就可以比较。例如:

1

2

3

4

5

6

7

8

9

var f *os.File

 

var r io.Reader = f

var rc io.ReadCloser = f

fmt.Println(r == rc) // true

 

var w io.Writer = f

// invalid operation: r == w (mismatched types io.Reader and io.Writer)

fmt.Println(r == w)

r的类型为io.Reader接口,rc的类型为io.ReadCloser接口。查看源码,io.ReadCloser的定义如下:

1

2

3

4

type ReadCloser interface {

    Reader

    Closer

}

io.ReadCloser可转化为io.Reader,故两者可比较。

io.Writer不可转化为io.Reader,编译报错。

总结

  • 可比较:int、ifloat、string、bool、complex、pointe、channel、interface、array
  • 不可比较:slice、map、function
  • 复合类型中如果带有不可比较的类型,那么该类型也是不可比较的。可以理解不可比较类型具有传递性。

以上就是分析go中的类型比较的详细内容,更多文章请关注木庄网络博客!!

相关阅读 >>

详解 go 语言中的方法

许式伟:go 语言有机会登顶,桌面侧亟待突破

马士兵【mca】高级架构师分享学习

golang 快速排序与 php 快速排序

golang基础数据类型-整型

golang 中const是什么

go-interface

golang入门三:类型

golang指针传递和值传递的区别是什么?

一周 go world 新鲜事

更多相关阅读请进入《golang》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...