当前位置 主页 > 网站技术 > 代码类 >

    Go中如何使用set的方法示例

    栏目:代码类 时间:2019-09-16 10:01

    今天来聊一下 Go 如何使用 set,本文将会涉及 set 和 bitset 两种数据结构。

    Go 的数据结构

    Go 内置的数据结构并不多。工作中,我们最常用的两种数据结构分别是 slice 和 map,即切片和映射。其实,Go 中也有数组,切片的底层就是数组,只不过因为切片的存在,我们平时很少使用它。

    除了 Go 内置的数据结构,还有一些数据结构是由 Go 的官方 container 包提供,如 heap 堆、list 双向链表和ring 回环链表。但今天我们不讲它们,这些数据结构,对于熟手来说,看看文档就会使用了。

    我们今天将来聊的是 set 和 bitset。据我所知,其他一些语言,比如 Java,是有这两种数据结构。但 Go 当前还没有以任何形式提供。

    实现思路

    先来看一篇文章,访问地址 2 basic set implementations[1] 阅读。文中介绍了两种 go 实现 set 的思路, 分别是 map 和 bitset。

    有兴趣可以读读这篇文章,我们接下来具体介绍下。

    map

    我们知道,map 的 key 肯定是唯一的,而这恰好与 set 的特性一致,天然保证 set 中成员的唯一性。而且通过 map 实现 set,在检查是否存在某个元素时可直接使用 _, ok := m[key] 的语法,效率高。

    先来看一个简单的实现,如下:

    set := make(map[string]bool) // New empty setset["Foo"] = true   // Addfor k := range set {   // Loop fmt.Println(k)}delete(set, "Foo") // Deletesize := len(set)  // Sizeexists := set["Foo"] // Membership

    通过创建 map[string]bool 来存储 string 的集合,比较容易理解。但这里还有个问题,map 的 value 是布尔类型,这会导致 set 多占一定内存空间,而 set 不该有这个问题。

    怎么解决这个问题?

    设置 value 为空结构体,在 Go 中,空结构体不占任何内存。当然,如果不确定,也可以来证明下这个结论。

    unsafe.Sizeof(struct{}{}) // 结果为 0

    优化后的代码,如下:

    type void struct{}var member voidset := make(map[string]void) // New empty setset["Foo"] = member   // Addfor k := range set {   // Loop fmt.Println(k)}delete(set, "Foo")  // Deletesize := len(set)  // Size_, exists := set["Foo"] // Membership

    之前在网上看到有人按这个思路做了封装,还写了一篇文章[2],可以去读一下。

    其实,github 上已经有个成熟的包,名为 golang-set,它也是采用这个思路实现的。访问地址 golang-set[3],描述中说 Docker 用的也是它。包中提供了两种 set 实现,线程安全的 set 和非线程安全的 set。

    演示一个简单的案例。

    package mainimport ( "fmt" mapset "github.com/deckarep/golang-set")func main() { // 默认创建的线程安全的,如果无需线程安全 // 可以使用 NewThreadUnsafeSet 创建,使用方法都是一样的。 s1 := mapset.NewSet(1, 2, 3, 4) fmt.Println("s1 contains 3: ", s1.Contains(3)) fmt.Println("s1 contains 5: ", s1.Contains(5)) // interface 参数,可以传递任意类型 s1.Add("poloxue") fmt.Println("s1 contains poloxue: ", s1.Contains("poloxue")) s1.Remove(3) fmt.Println("s1 contains 3: ", s1.Contains(3)) s2 := mapset.NewSet(1, 3, 4, 5) // 并集 fmt.Println(s1.Union(s2))}

    输出如下:

    s1 contains 3:  true
    s1 contains 5:  false
    s1 contains poloxue:  true
    s1 contains 3:  false
    Set{4, polxue, 1, 2, 3, 5}