首頁  >  文章  >  後端開發  >  分析go中的類型比較

分析go中的類型比較

藏色散人
藏色散人轉載
2021-06-21 16:09:533294瀏覽

以下由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等高階語言自動幫我們進行型別轉換,所以我們在比較時必須用==兩邊的型別必須一致,即使他們底部類型一致也不行。看下面的程式碼

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的基本型別就比較簡單,只要型別是一樣的,那麼他們就是可以比較的,舉個栗子:

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了

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則相反。那麼數組是否可以比較呢,看下面的例子:

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的比較也從內部型別開始比較,每一個類型的值相等才是相等的。如下例:

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結構體是可以比較的嗎。我們再來看個例子:

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也被定義成不可比較型別。那麼引用型別都是不可比較嗎?也不是,看個例子:

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{} 类型
type eface struct { // 16 字节
    _type *_type
    data  unsafe.Pointer
}

type iface struct { // 16 字节
    tab  *itab
    data unsafe.Pointer
}

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

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,故两者不等。

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不可比较的类型,那么会直接报错的。看个例子:

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

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

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的定义如下:

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中的類型比較的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除