在Go语言中处理集合(Set)的方法和测试方式

首先

Go语言中没有默认提供集合(Set)。

实现方式

1. 切片 (qiē

strSet1 := []string{"element1", "element2", "element3"}

简单的方法。
允许重复,只是一个切片。
对于简单的情况,这就足够了。

2. 映射[接口{}]键结构{}

strSet2 := map[string]struct{}{
    "element1": struct{}{},
    "element2": struct{}{},
    "element3": struct{}{},
}

mapを利用する方法。
struct{}はメモリを圧迫することはない。

3. github.com/deckarep/golang-set/mapset 的网址

strSet3 := mapset.NewSet()
requiredClasses.Add("element1")
requiredClasses.Add("element2")
requiredClasses.Add("element3")

使用包的方法。
有许多方便的方法来操作Set。
内部实现使用了map[interface{}]struct{}。
在https://github.com/deckarep/golang-set上有源代码。

用途的定义是指某物品或事物的目标、目的或功能。

在进行个人开发时,需要对Redis的SMembers功能进行测试。
Redis可以使用Set来存储数据,并且可以使用SMembers来获取数据。
由于go语言中没有Set类型,go-redis的SMembers返回的是[]string。
当需要比较集合时,使用[]string会导致元素之间的顺序无法保证,从而导致测试失败,因此需要使用Set类型。
我认为使用第3种方法的github.com/deckarep/golang-set/mapset过于夸张,所以选择了第2种方法,即使用map[interface{}]struct{}。
如果要将[]string作为集合进行比较,那么为了确保每个元素都包含在内,可能需要进行大量的for循环。

package sample

import (
    "testing"

    "github.com/go-redis/redis/v7"
)

type RepositoryImpl struct {
    Client *redis.Client
}

func New(addr string) (*redis.Client, error) {
    client := redis.NewClient(&redis.Options{
        Addr: addr,
    })
    if err := client.Ping().Err(); err != nil {
        return nil, errors.Wrapf(err, "failed to ping redis server")
    }
    return client, nil
}

func (r RepositoryImpl) GetStringSet(key string) ([]string, error) {
    result, err := r.Client.SMembers(key).Result()
    if err != nil {
        return nil, err
    }
    return result, nil
}
package sample

import (
    "testing"

    "github.com/go-redis/redis/v7"
)

func NewMockRedis(t *testing.T) *redis.Client {
    t.Helper()

    s, err := miniredis.Run()
    if err != nil {
        t.Fatalf("unexpected error while createing test redis server '%#v'", err)
    }

    client := redis.NewClient(&redis.Options{
        Addr: s.Addr(),
    })
    return client
}


func TestGetStringSet(t *testing.T) {
    client := NewMockRedis(t)
    r := RepositoryImpl{
        Client: client,
    }

    client.SAdd("testKey", "hoge", "fuga", "piyo")

    actual, err := r.GetStringSet("testKey")
    if err != nil {
        t.Fatalf("unexpected error while GetStringSet '%#v'", err)
    }
    expected, err := []string{"hoge", "fuga", "piyo"}

    // 順番が担保されないため、テストが失敗する
    if diff := cmp.Diff(actual, expected); diff != "" {
        t.Errorf("Diff: (-got +want)\n%s", diff)
    }

    // sliceではcmp.Diffで順序が考慮されてしまうのでSetに変換して比較する
    expectedSet := make(map[string]struct{})
    for _, v := range expected {
        expectedSet[v] = struct{}{}
    }
    actualSet := make(map[string]struct{})
    for _, v := range actual {
        actualSet[v] = struct{}{}
    }

    // 順番が担保されるため、テストが成功する
    if diff := cmp.Diff(actualSet, expectedSet); diff != "" {
        t.Errorf("Diff: (-got +want)\n%s", diff)
    }
}

请提供更多上下文说明或具体的句子,以便更好地把握需要重新表达的意思。

实现Set类型在Go中的选择。

广告
将在 10 秒后关闭
bannerAds