Golang 之 struct能不能比較

第8期 距離大叔的80期小目標還有72期,今天大叔要跟你們分享的依舊是golang的基礎知識點——struct能不能比較,這個基礎問題很考驗你們基礎和細節,也是面試官比較喜歡問的問題,接下來跟你們一塊兒來了解一下吧。golang

struct能不能比較? 很顯然這句話包含了兩種狀況:web

  • 同一個struct的兩個實例能不能比較?
  • 兩個不一樣的struct的實例能不能比較?

劃重點

在分析上面兩個問題前,先跟你們梳理一下golang中,哪些數據類型是可比較的,哪些是不可比較的:面試

  • 可比較:IntegerFloating-pointStringBooleanComplex(複數型)PointerChannelInterfaceArray
  • 不可比較:SliceMapFunction

下面就跟你們分別分析一下上面兩種狀況吧數組

同一個struct的兩個實例能不能比較

首先,咱們構造一個struct結構體來玩玩吧markdown

type S struct {
 Name string  Age int  Address *int }  func main() {  a := S{  Name: "aa",  Age: 1,  Address: new(int),  }  b := S{  Name: "aa",  Age: 1,  Address: new(int),  }   fmt.Println(a == b) } 複製代碼

運行上面的代碼發現會打印false。既然能正常打印輸出,說明是能夠個比較的,接下來讓咱們來個死亡兩問app

什麼能夠比較?ide

回到上面的劃重點部分,在總結中咱們能夠知道,golang中 SliceMapFunction 這三種數據類型是不能夠直接比較的。咱們再看看S結構體,該結構體並無包含不可比較的成員變量,因此該結構體是能夠直接比較的。函數

爲何打印輸出false?oop

a 和 b 雖然是同一個struct 的兩個實例,可是由於其中的指針變量 Address 的值不一樣,因此 a != b,若是a b 在初始化時把 Address 去掉(不給 Address 初始化),那麼這時 a == b 爲true, 由於ptr變量默認值是nil,又或者給 Address 成員變量賦上同一個指針變量的值,也是成立的。ui

若是給結構體S增長一個Slice類型的成員變量後又是什麼狀況呢?

type S struct {
 Name string  Age int  Address *int  Data []int }  func main() {  a := S{  Name: "aa",  Age: 1,  Address: new(int),  Data: []int{1, 2, 3},  }  b := S{  Name: "aa",  Age: 1,  Address: new(int),  Data: []int{1, 2, 3},  }   fmt.Println(a == b) } 複製代碼

這時候會打印輸出什麼呢?true?false?實際上運行上面的代碼會報下面的錯誤:

# command-line-arguments
./test.go:37:16: invalid operation: a == b (struct containing []int cannot be compared) 複製代碼

a, b 雖然是同一個struct兩個賦值相同的實例,由於結構體成員變量中帶有了不能比較的成員(slice),是不能夠直接用 == 比較的,因此只要寫 == 就報錯

總結

同一個struct的兩個實例可比較也不可比較,當結構不包含不可直接比較成員變量時可直接比較,不然不可直接比較


但在平時的實踐過程當中,當咱們須要對含有不可直接比較的數據類型的結構體實例進行比較時,是否是就無法比較了呢?事實上並不是如此,golang仍是友好滴,咱們能夠藉助 reflect.DeepEqual 函數 來對兩個變量進行比較。因此上面代碼咱們能夠這樣寫:

type S struct {
 Name string  Age int  Address *int  Data []int }  func main() {  a := S{  Name: "aa",  Age: 1,  Address: new(int),  Data: []int{1, 2, 3},  }  b := S{  Name: "aa",  Age: 1,  Address: new(int),  Data: []int{1, 2, 3},  }   fmt.Println(reflect.DeepEqual(a, b)) } 複製代碼

打印輸出:

true
複製代碼

那麼 reflect.DeepEqual 是如何對變量進行比較的呢?

reflect.DeepEqual

DeepEqual函數用來判斷兩個值是否深度一致。具體比較規則以下:

  • 不一樣類型的值永遠不會深度相等
  • 當兩個數組的元素對應深度相等時,兩個數組深度相等
  • 當兩個相同結構體的全部字段對應深度相等的時候,兩個結構體深度相等
  • 當兩個函數都爲nil時,兩個函數深度相等,其餘狀況不相等(相同函數也不相等)
  • 當兩個interface的真實值深度相等時,兩個interface深度相等
  • map的比較須要同時知足如下幾個
    • 兩個map都爲nil或者都不爲nil,而且長度要相等
    • 相同的map對象或者全部key要對應相同
    • map對應的value也要深度相等
  • 指針,知足如下其一便是深度相等
    • 兩個指針知足go的==操做符
    • 兩個指針指向的值是深度相等的
  • 切片,須要同時知足如下幾點纔是深度相等
    • 兩個切片都爲nil或者都不爲nil,而且長度要相等
    • 兩個切片底層數據指向的第一個位置要相同或者底層的元素要深度相等
    • 注意:空的切片跟nil切片是不深度相等的
  • 其餘類型的值(numbers, bools, strings, channels)若是知足go的==操做符,則是深度相等的。要注意不是全部的值都深度相等於本身,例如函數,以及嵌套包含這些值的結構體,數組等

兩個不一樣的struct的實例能不能比較

結論:能夠比較,也不能夠比較

可經過強制轉換來比較:

type T2 struct {
 Name string  Age int  Arr [2]bool  ptr *int }  type T3 struct {  Name string  Age int  Arr [2]bool  ptr *int }  func main() {  var ss1 T2  var ss2 T3  // Cannot use 'ss2' (type T3) as type T2 in assignment  //ss1 = ss2 // 不一樣結構體之間是不能夠賦值的  ss3 := T2(ss2)  fmt.Println(ss3==ss1) // true } 複製代碼

若是成員變量中含有不可比較成員變量,即便能夠強制轉換,也不能夠比較

type T2 struct {
 Name string  Age int  Arr [2]bool  ptr *int  map1 map[string]string }  type T3 struct {  Name string  Age int  Arr [2]bool  ptr *int  map1 map[string]string }  func main() {  var ss1 T2  var ss2 T3   ss3 := T2(ss2)  fmt.Println(ss3==ss1) // 含有不可比較成員變量 } 複製代碼

編譯報錯:

# command-line-arguments
./test.go:28:18: invalid operation: ss3 == ss1 (struct containing map[string]string cannot be compared) 複製代碼

問:struct能夠做爲map的key麼

struct必須是可比較的,才能做爲key,不然編譯時報錯

type T1 struct {
 Name string  Age int  Arr [2]bool  ptr *int  slice []int  map1 map[string]string }  type T2 struct {  Name string  Age int  Arr [2]bool  ptr *int }  func main() {  // n := make(map[T2]string, 0) // 無報錯  // fmt.Print(n) // map[]   m := make(map[T1]string, 0)  fmt.Println(m) // invalid map key type T1 } 複製代碼

上面就是今天要跟你們分享的內容,有什麼問題歡迎你們在後臺給大叔留言,關注大叔說碼,咱們下期見~

參考文檔:

  1. https://studygolang.com/pkgdoc
  2. https://studygolang.com/articles/12944?fr=sidebar
相關文章
相關標籤/搜索