[Go]泛型约束与集合Set实现


go1.8中引入了泛型,以通用类型的形式表示函数和数据结构。

泛型约束

在非泛型函数中,传递给 interface 形参的实参必须实现 interface 中的所有方法;而泛型函数中,传入的类型实参必须满足类型形参的约束条件,即泛型代码只能使用约束条件允许的操作

type Stringer interface {
 String() string
}

func Stringify[T Stringer](s []T) (ret []string) {
 for _, v := range s {
  ret = append(ret, v.String())
 }
 return ret
}

// 使用:
func (s *StrTest) String() string {
	return s.Item
}
s := []*StrTest{
	{"12"},
	{"34"},
}
fmt.Println(Stringify(s))

除此之外,泛型约束中还可添加约束元素;在go中有以下约束方法:

// 没有任何约束
func add[T any](x, y T) T
// 约束 Addable(通过约束接口定义Addable)
func add[T Addable](x, y T) T
// 约束:允许int或float64类型
func add[T int|float64](x, y T) T
// 约束允许底层类型是 string 的类型(包括 string 类型)
func add[T ~string](x, y T) T

类型并集

通过扩展interface实现类型约束:

  • |:组合不同类型(即,并集);
    • |连接的类型不能有交集(接口除外):即不允许int | ~int
    • 并集成员大于1时,不能直接或间接引入comparable接口;
  • ~:放在类型前面,表示所有底层类型是此类型的类型;
    • ~后面的类型不能为接口
    • ~后面的类型必须为基本类型

所有整数的约束:

type Int interface {
    int | int8 | int16 | int32 | int64
}

所有底层类型是有序类型(如:type int MyInt)的约束:

type OrderedType interface {
	Integer | Float | ~string
}

type Integer interface {
	Signed | Unsigned
}

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

type Float interface {
	~float32 | ~float64
}

类型交集

约束接口中类型可以有多行,若有多行,则取他们的交集:

// 约束为int:因int与~int交集为int
type C interface {
    ~int
    int
}

// 约束为空;因int和float32没有相交的类型
type Bad interface {
    int
    float32 
} 

comparable

comparable是go新引入的预定义标识符(一个接口),指代可以使用==或!=来进行比较的类型集合。

any

泛型中引入了any,表示可以接收任意类型;其实any定义即为一个空接口(即interface{}

集合SET

go中没提供集合set(存储的元素是唯一的),但可通过map方便实现;为减少空间占用,其值可用空结构(struct{},go会优化为不占用空间)。

type EmptyType struct{}

var empty EmptyType

map中的key是唯一,所以set中的元素即为map中的key;因map中key要是可比较的,所以需要约束为comparable:

// Set 集合类型,不要直接构造,通过NewSet
type Set[T comparable] struct {
	m map[T]EmptyType
}

// NewSet 构造Set
func NewSet[T comparable]() *Set[T] {
	return &Set[T]{
		m: make(map[T]EmptyType),
	}
}

// NewSetWith 构造有元素的Set
func NewSetWith[T comparable](items ...T) *Set[T] {
	s := &Set[T]{
		m: make(map[T]EmptyType, len(items)),
	}
	for _, v := range items {
		s.m[v] = empty
	}
	return s
}

// Add 添加元素
func (s *Set[T]) Add(val T) {
	s.m[val] = empty
}

// Remove 删除元素
func (s *Set[T]) Remove(val T) {
	delete(s.m, val)
}

// Contains 检测是否包含元素
func (s *Set[T]) Contains(val T) bool {
	_, ok := s.m[val]
	return ok
}

// Equals 判断两集合是否相等
func (s *Set[T]) Equals(other *Set[T]) bool {
	if other == nil || s.Len() != other.Len() {
		return false
	}

	for k := range s.m {
		if _, ok := other.m[k]; !ok {
			return false
		}
	}
	return true
}

// Len 获取长度
func (s *Set[T]) Len() int {
	return len(s.m)
}

// Clear 清空set
func (s *Set[T]) Clear() {
	s.m = make(map[T]EmptyType)
}

// Items 遍历set
func (s *Set[T]) Items() []T {
	item := make([]T, 0)
	for k := range s.m {
		item = append(item, k)
	}
	return item
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值