Golang仅用两种内置数据结构的核心设计思想

Golang仅用两种内置数据结构的核心设计思想

  1. Go 仅提供了切片和映射这两种内置数据结构。这比 C 语言(仅数组)多,但比 Python、C++ 和 Swift 少。我认为这很棒,它使得大家的代码看起来都有些相似。但我仍然想知道,设计这两种内置数据结构背后的理念是什么。

  2. 当我想要检查一个自定义结构体类型的键是否存在于映射中时,为什么它不需要某种哈希或相等接口?例如,如果我要对自定义结构体的切片进行排序,我将需要定义排序所需的 Len、Less、Swap 方法。

谢谢

package main

import (
	"fmt"
)

type K struct {
	name string
	num  int
}

func main() {
	d := map[K]struct{}{}
	d[K{"ccc", 3}] = struct{}{}
	d[K{"bbb", 2}] = struct{}{}
	d[K{"aaa", 1}] = struct{}{}

	k := K{"bbb", 2}
	v, ok := d[k]
	fmt.Println(v, ok)  //true
}

更多关于Golang仅用两种内置数据结构的核心设计思想的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

谢谢克里斯托夫

更多关于Golang仅用两种内置数据结构的核心设计思想的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是正确的,但这取决于结构体的字段。如果你添加了一个不可比较的字段,那么整个结构体就不再可比较

感谢Christoph,

我刚发现这两个运算符是为结构体定义的,但小于或大于运算符则不是。不过在C++中,结构体默认是不可比较的。

type K struct {
	name string
	num  int
}

func main() {
	k1 := K{"bbb", 2}
	k2 := K{"bbb", 2}

	fmt.Println("k1 == k2", k1 == k2) //true
	fmt.Println("k1 != k2", k1 != k2) //false
	// fmt.Println("k1 < k2", k1 < k2) //not defined
	// fmt.Println("k1 > k2", k1 > k2) //not defined
}
  1. 我认为这里的主要设计原则是极简主义。基本类型、数组/切片、映射、结构体和接口几乎就是你构建更复杂数据结构所需的全部。

    我不太清楚Python或Swift除了映射和(动态)数组之外还提供了哪些类型,但我敢打赌,这些类型可以毫不费力地用Go的内置类型来表达。因此,没有令人信服的理由将它们包含在核心语言中。

  2. 映射的键必须是可比较的类型。也就是说,只有定义了 ==!= 操作符的类型才能用作映射键。因此,不需要额外的语言构造来测试一个键是否存在于映射中。

    另一方面,切片可以容纳任何类型的数据。与映射不同,切片没有理由将元素类型限制为可排序类型。因此,如果你想对具有自定义元素类型的切片进行排序,你必须实现自己的排序算法。Len/Less/Swap接口可以帮助你做到这一点。

// 示例:实现 sort.Interface 进行自定义排序
type ByLength []string

func (s ByLength) Len() int {
    return len(s)
}
func (s ByLength) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}
func (s ByLength) Less(i, j int) bool {
    return len(s[i]) < len(s[j])
}

Go语言仅提供切片和映射作为内置数据结构,这体现了其“简单性”和“实用性”的设计哲学。切片提供了动态数组的能力,映射提供了键值对存储,这两种结构覆盖了大多数实际开发场景。这种设计减少了语言复杂性,同时通过标准库的container包(如listheap)支持其他数据结构,保持了灵活性。

关于映射键的检查,Go要求键类型必须是可比较的(comparable),这意味着类型必须支持==!=操作。对于自定义结构体,如果其所有字段都是可比较的,那么结构体本身也是可比较的,因此可以直接用作映射键,无需显式实现哈希或相等接口。这是因为Go在编译时自动为可比较类型生成了必要的比较逻辑。例如:

package main

import "fmt"

type Key struct {
    Name string
    ID   int
}

func main() {
    m := make(map[Key]string)
    k1 := Key{"Alice", 1}
    m[k1] = "value1"
    
    k2 := Key{"Alice", 1}
    value, exists := m[k2] // 自动比较k1和k2的字段值
    fmt.Println(value, exists) // 输出: value1 true
}

相比之下,排序需要实现sort.Interface,因为排序规则(如Less方法)是业务逻辑相关的,无法由编译器自动推断。映射键的比较是值等性检查,而排序涉及顺序逻辑,这解释了为什么两者设计不同。

回到顶部