Übersicht
In einem kürzlich durchgeführten Interview fragte der Interviewer nach dem Typvergleich zwischen go. Grundsätzlich ist die Grundlage nicht stark genug! Ich habe eine Menge Informationen im Internet gelesen und selbst ein paar einfache Zusammenfassungen erstellt, haha!
Typen in go
Werfen wir zunächst einen Blick auf die grundlegendsten zentralisierten Typen, die in go enthalten sind
- Grundlegende Typen: Zu den grundlegendsten Typen in go gehören Ganzzahlen (
int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, byte, rune
usw.), Gleitkommatyp (float32, float64
), String (string
ist ebenfalls vorhanden). a []rune Arrays) und weniger häufig verwendete komplexe Typen (complex64/complex128
).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中Slice
和Map
被定义成不能比较的类型。我们来看
如果Slice
是可比较,那么用什么来定义是一样的切片呢?如果用地址,那么如果两个地址指向的Slice
是一样的呢?这显然不合适。如果和数组一样的方式,那么我切片扩容了呢,就不相等了。所以长度和容量导致不好比较。虽然可以在语言层面解决这个问题,但是 golang 团队认为不值得为此耗费精力。所以Slice
被当成不可比较。
同样的Map
Struktur
und Array
. Referenztypen: Slice, Map, Channel, Pointer
. - Schnittstellentyp:
Fehler, io.Reader usw.
. - Go als stark typisierte Sprache hilft uns nicht automatisch bei der Typkonvertierung mit Hochsprachen wie PHP, daher müssen wir beim Vergleich die Typen auf beiden Seiten verwenden Seien Sie konsistent, auch wenn ihre unteren Typen konsistent sind. Schauen Sie sich den Code unten an
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 }
Go führt also keine implizite Konvertierung für uns durch. Auch wenn die zugrunde liegenden Typen konsistent sind, können sie nicht verglichen werden.
Als nächstes analysieren wir, ob Vergleiche verschiedener Typen durchgeführt werden können.
Basistypen
🎜Gos Basistypen sind relativ einfach, dann können sie verglichen werden: 🎜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
Array
🎜Der Unterschied zwischen Go-Arrays und Slices wird oft in Interviews gefragt . Die Länge eines Arrays in Go muss zuerst bestimmt werden, das heißt, die Länge kann nicht erweitert werden. Und es handelt sich um eine Wertkopie. Wenn die Parameter an eine Funktion übergeben und geändert werden, bleibt der externe Wert gleich. Slice ist das Gegenteil. Ob Arrays verglichen werden können, sehen Sie sich das folgende Beispiel an: 🎜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
Arrays gleicher Länge verglichen werden können, Arrays unterschiedlicher Länge jedoch nicht verglichen werden können
. Was ist der Grund? Dies liegt daran, dass im Array-Typ auch die Länge des Arrays Teil des Typs ist. Arrays unterschiedlicher Länge werden als unterschiedliche Typen betrachtet und können daher nicht verglichen werden. 🎜Struktur
🎜Die gleicheStruktur
ist dieselbe. Der Vergleich von Struct
beginnt ebenfalls mit dem internen Typ, und die Werte jedes Typs sind nur dann gleich, wenn sie gleich sind. Das folgende Beispiel: 🎜var a interface{} = []int{1, 2} var b interface{} = []int{1, 2} // panic: runtime error: comparing uncomparable type []int fmt.Println(a == b)
Struct
-Strukturen vergleichbar sind. Schauen wir uns ein anderes Beispiel an: Wie ist 🎜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)
Slice
und Map
als nicht vergleichbare Typen definiert. Werfen wir einen Blick darauf🎜🎜Wenn Slice
vergleichbar ist, was wird dann verwendet, um dieselben Slices zu definieren? Wenn Adressen verwendet werden, was passiert, wenn der Slice
, auf den die beiden Adressen zeigen, identisch ist? Das ist offensichtlich unangemessen. Wenn es mit dem Array identisch ist und ich das Slice erweitere, ist es nicht gleich. Daher sind Länge und Kapazität schwer zu vergleichen. Obwohl dieses Problem auf Sprachebene gelöst werden kann, ist das Golang-Team der Ansicht, dass sich der Aufwand nicht lohnt. Daher wird Slice
als unvergleichbar behandelt. Die gleiche
Map
wird auch als unvergleichlicher Typ definiert. Sind Referenztypen also nicht vergleichbar? Nein, schauen Sie sich ein Beispiel an: 🎜type ReadCloser interface { Reader Closer }
- 使用
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
a
和c
类型相同(都是int
),值也相同(都是0
,基本类型比较),故两者相等。 a
和b
类型相同,值不等,故两者不等。 a
和d
类型不同,a
为int
,d
为float64
,故两者不等。
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
a
和b
类型相同(都是A
),值也相同(结构体A
),故两者相等。 a
和c
类型相同,值不同,故两者不等。 d
和e
类型相同(都是*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)
a
和b
的类型是切片类型,而切片类型不可比较,所以a == b
会panic
。
接口值的比较不要求接口类型(注意不是动态类型)完全相同,只要一个接口可以转化为另一个就可以比较。例如:
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
- 复合类型中如果带有不可比较的类型,那么该类型也是不可比较的。可以理解不可比较类型具有传递性。