文章目录
本节项目地址:06-GenericsQueue
泛型
如果你经常要分别为不同的类型写完全相同逻辑的代码,那么使用泛型将是最合适的选择
类型参数
类型形参列表:T int | float64
go
func min[T int | float64](a, b T) T {
if a <= b {
return a
}
return b
}
func function01() {
x := min[int](10, 20)
y := min[float64](0.1, -0.2)
fmt.Println(x, y) // 10 -0.2
}
类型形参:T
,类型实参:int
类型实例化
向 min
函数提供类型参数(在本例中为 int
和 float64
)称为实例化。
类型实例化:
- 编译器在整个泛型函数或类型中,将所有类型形参替换为它们各自的类型实参。
- 编译器验证每个类型参数是否满足相应的约束。
go
fmin := min[float64] // 类型实例化
z := fmin(1.1, 2.2)
fmt.Println(z) // 1.1
类型约束
Go支持将类型约束单独拿出来定义到接口中,从而让代码更容易维护。
go
type Int interface {
int | int8 | int16 | int32 | int64
}
type Uint interface {
uint | uint8 | uint16 | uint32
}
type Float interface {
float32 | float64
}
type Slice[T Int | Uint | Float] []T // 使用 '|' 将多个接口类型组合
两种常见的类型约束:
类型约束字面量 :通常 interface{}
可省略
go
func max[T interface{int | float64}](a, b T) T {
if a >= b {
return a
}
return b
}
约束类型:事先定义,并支持复用
go
type Value interface {
int | float64
}
func max1[T Value](a, b T) T {
if a >= b {
return a
}
return b
}
几种语法错误:
- 定义泛型类型,基础类型不能只有类型形参:
go
// 类型形参不能单独使用
type Type[T int | float32] T
- 类型约束语义被编译器误认为是表达式:
go
// 误认为定义一个存放切片的数组,长度 T * int
type NewType [T * int][]T
// | 误以为是按位或
type NewType[T *int | *float32] []T
解决办法:可使用 interface{}
包裹或加上逗号:
go
type NewType[T interface{*int}] []T
type NewType[T interface{*int | *float64}] []T
// 类型约束只有一个类型,可以添加逗号消除歧义
type NewType[T *int,] []T
类型集
从 Go 1.18 开始,一个接口不仅可以嵌入其他接口,还可以嵌入任何类型、类型的联合或共享相同底层类型的无限类型集合。
当用作类型约束时,由接口定义的类型集精确地指定允许作为相应类型参数的类型。
|
符号
T1 | T2
表示类型约束为 T1
和 T2
这两个类型的并集
~
符号
~T
表示所有底层类型是 T
的类型集合,~
后面只能是基本类型
go
type Map[K int | string, V float32 | float64] map[K]V
type IValue interface {
~string | ~int
}
type MyInt int
func sum[T IValue](a []T) (res T) {
for _, e := range a {
res += e
}
return
}
func function04() {
m := Map[int, float64]{}
m[1] = 1.0
m[2] = 2.0
fmt.Println(m) // map[1:1 2:2]
a := []MyInt{1, 2, 3}
fmt.Println(sum(a)) // 6
}
并集 & 交集
go
// 类型并集
type Int interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}
type Uint interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}
// 类型交集
type A interface {
Int
Uint
~string
}
- 类型取并集时,用
|
连接多个类型,这些类型必须不交集,相交类型是接口不受约束
go
// 错误,overlapping terms ~int and int
type B interface {
int | ~int
}
// 正确
type B interface {
int | interface{ ~int }
}
- 并集中不能有类型实参
go
// 错误
type C[T int | string] interface {
~float64 | T
}
- 接口不能直接或间接并入自己
go
// 错误,D嵌入E,间接并入了自己
type D interface {
E
}
type E interface {
D
}
// 错误
type F interface {
int | ~float64 | F
}
- 带方法的接口,不能写入接口的并集中
go
type ReadWriter[T any] interface {
Read(data T) (newData T)
Writer(data T) error
}
// 错误
type G interface {
int | ReadWriter[int]
}
泛型接收者
定义一个新类型后,可以给新类型添加方法,那么同样也可以给泛型类型添加方法,如下:
- 本例子为泛型类型
MyFloat[T]
定义了类型方法avg
,计算数组平均值 - 在使用泛型类型前,始终需要先对类型实参实例化
go
type MyFloat[T ~float32 | ~float64] []T
func (mf MyFloat[T]) avg() T {
var res T
for _, e := range mf {
res += e
}
return res / T(len(mf))
}
func function05() {
var fa MyFloat[float32] = []float32{1.0, 2.1, 3.2}
fmt.Println(fa.avg()) // 2.1000001
f64 := MyFloat[float64]{1.0, 2.1, 3.2}
fmt.Println(f64.avg()) // 2.1
}
泛型方法
Go 目前不支持泛型方法,如下:
go
type MyStruct struct {}
// 错误:Method cannot have type parameters
func (ms MyStruct) Add[T int | string](other T) MyStruct {}
在方法中使用泛型,可以通过接收者来使用,如下:
go
type MyStruct[T int | string] struct {
value T
}
func (ms MyStruct[T]) Add(other T) MyStruct[T] {
ms.value += other
return ms
}
func function06() {
a := MyStruct[int]{1}
b := MyStruct[string]{"1"}
fmt.Println(a.Add(2), b.Add("34")) // {3} {134}
}
泛型接口
两种接口类型
-
基本接口:接口中只有方法
-
一般接口:接口中不只有方法,还有类型
一般接口不能用来定义变量,只能用于泛型的类型约束中。
go
// 基本接口
type MyErr interface {
Error() string
}
// 一般接口
type MyUint interface {
~uint | ~uint8 | ~uint16
}
func function07() {
var a MyErr = fmt.Errorf("这是基本接口")
fmt.Println(a.Error()) // 这是基本接口
// 错误:不能使用一般接口定义变量
// var b MyUint
}
泛型接口
泛型接口,Processer[T]
在实例化后,即为基本接口,可以用于创建变量,如下 XML
类型实现了 Processer[string]
接口:
go
type Processer[T any] interface {
Process(data T) T
Save(data T) error
}
type XML struct{}
func (x XML) Process(data string) (newData string) {
return ""
}
func (x XML) Save(data string) error {
return errors.New("")
}
func function08() {
var a Processer[string] = XML{}
a.Process("")
a.Save("")
}
泛型接口,ProcesserNormal[T]
实例化后,即为一般接口,只能用于类型约束,不能用于定义变量。笔者没探索出来有啥用 ~O(∩_∩)O~
go
type ProcesserNormal[T any] interface {
int | ~struct{ value T }
Process(data T) T
Save(data T) error
}
其余需要注意的:
- 匿名结构体不支持泛型
go
type TypeStruct[T int | string] struct {
Name string
Data []T
}
func function03() {
x := TypeStruct[int]{"Cauchy", []int{1, 2, 3}}
fmt.Println(x) // {Cauchy [1 2 3]}
/* 匿名结构体不支持泛型
y := struct[T []int|[]string]{
Name string
Data T
}[int]{
"AQ",
[]int{1, 2, 3},
}
*/
}
- 当你需要针对不同类型书写同样的逻辑,使用泛型来简化代码是最好的 (比如你想写个队列,写个链表、栈、堆之类的数据结构)
泛型实现通用数据结构
通用数据结构实现参考:https://pkg.go.dev/github.com/emirpasic/gods/v2
数组链表
下述实现,提及 Go 内置的一个可比较对象类型 ------ comparable
接口。comparable
接口是由所有可比较类型实现的接口,只能用作类型参数约束,不能用作变量类型。
- 泛型容器接口
containers/containers.go
,所有容器都需要实现其内部方法:
go
type Container[T any] interface {
Empty() bool
Size() int
Clear()
Values() []T
String() string
}
- 泛型链表接口
lists/lists.go
,所有链表都要实现其内部方法:
go
type List[T comparable] interface {
Get(index int) (T, bool)
Remove(index int)
Add(values ...T)
Contains(values ...T) bool
Sort(comparator utils.Comparator[T])
Insert(index int, values ...T)
Set(index int, value T)
containers.Container[T]
}
- 泛型可比较对象
utils/comparator.go
:
go
type Comparator[T any] func(x, y T) int
- 泛型数组链表
lists/arraylist/arraylist.go
:
go
package arraylist
import (
"06-GenericsTest/lists"
"06-GenericsTest/utils"
"fmt"
"slices"
"strings"
)
// 断言 List 实现接口 lists.List
var _ lists.List[int] = (*List[int])(nil)
// List 链表结构
type List[T comparable] struct {
elements []T
size int
}
const (
growthFactor = float32(2.0) // 扩容因子
shrinkFactor = float32(0.25) // 缩容因子
)
// New 创建链表
func New[T comparable](values ...T) *List[T] {
list := &List[T]{}
if len(values) > 0 {
list.Add(values...)
}
return list
}
// Add 向链表末尾添加元素
func (l *List[T]) Add(values ...T) {
l.growBy(len(values))
for _, value := range values {
l.elements[l.size] = value
l.size++
}
}
// Get 获取下标 index 的元素
func (l *List[T]) Get(index int) (T, bool) {
if !l.withinRange(index) {
var t T
return t, false
}
return l.elements[index], true
}
// Remove 移除下标 index 的元素
func (l *List[T]) Remove(index int) {
if !l.withinRange(index) {
return
}
clear(l.elements[index : index+1])
copy(l.elements[index:], l.elements[index+1:l.size])
l.size--
l.shrink()
}
// Contains 判断是否包含元素
func (l *List[T]) Contains(values ...T) bool {
for _, searchValue := range values {
found := false
for i := 0; i < l.size; i++ {
if l.elements[i] == searchValue {
found = true
break
}
}
if !found {
return false
}
}
return true
}
// Values 获取链表元素切片
func (l *List[T]) Values() []T {
newElements := make([]T, l.size, l.size)
copy(newElements, l.elements[:l.size])
return newElements
}
// IndexOf 获取元素所在下标
func (l *List[T]) IndexOf(value T) int {
if l.size == 0 {
return -1
}
for index, element := range l.elements {
if element == value {
return index
}
}
return -1
}
// Empty 判断链表为空
func (l *List[T]) Empty() bool {
return l.size == 0
}
// Size 获取元素个数
func (l *List[T]) Size() int {
return l.size
}
// Clear 清空链表
func (l *List[T]) Clear() {
l.size = 0
l.elements = []T{}
}
// Sort 链表元素排序
func (l List[T]) Sort(comparator utils.Comparator[T]) {
if len(l.elements) < 2 {
return
}
slices.SortFunc(l.elements[:l.size], comparator)
}
// Swap 交换两个元素
func (l *List[T]) Swap(i, j int) {
if l.withinRange(i) && l.withinRange(j) {
l.elements[i], l.elements[j] = l.elements[j], l.elements[i]
}
}
// Insert 指定位置插入
func (l *List[T]) Insert(index int, values ...T) {
if !l.withinRange(index) {
if index == l.size {
l.Add(values...)
}
return
}
length := len(values)
l.growBy(length)
l.size += length
copy(l.elements[index+length:], l.elements[index:l.size-length])
copy(l.elements[index:], values)
}
// Set 指定位置设置元素
func (l *List[T]) Set(index int, value T) {
if !l.withinRange(index) {
if index == l.size {
l.Add(value)
}
return
}
l.elements[index] = value
}
// String 字符串显示链表
func (l *List[T]) String() string {
str := "ArrayList: "
values := make([]string, 0, l.size)
for _, value := range l.elements[:l.size] {
values = append(values, fmt.Sprintf("%v", value))
}
str += "[" + strings.Join(values, ", ") + "]"
return str
}
// withinRange 判断是否越界
func (l *List[T]) withinRange(index int) bool {
return index >= 0 && index < l.size
}
// resize 链表底层切片重新分配地址
func (l *List[T]) resize(cap int) {
newElements := make([]T, cap)
copy(newElements, l.elements)
l.elements = newElements
}
// growBy 判断是否增加 n 各元素需要扩容
func (l *List[T]) growBy(n int) {
currentCapacity := cap(l.elements)
if l.size+n >= currentCapacity {
newCapacity := int(growthFactor * float32(currentCapacity+n))
l.resize(newCapacity)
}
}
// shrink 缩容
func (l *List[T]) shrink() {
if shrinkFactor == 0.0 {
return
}
currentCapacity := cap(l.elements)
if l.size <= int(float32(currentCapacity)*shrinkFactor) {
l.resize(l.size)
}
}
数组队列
- 泛型队列接口
queues/queues.go
:
go
// Queue 泛型队列接口
type Queue[T comparable] interface {
Enqueue(value T)
Dequeue() (value T, ok bool)
Peek() (value T, ok bool)
containers.Container[T]
}
- 泛型数组队列
queues/arrayqueue/arrayqueue.go
:
go
package arrayqueue
import (
"06-GenericsTest/lists/arraylist"
"06-GenericsTest/queues"
"fmt"
"strings"
)
var _ queues.Queue[int] = (*Queue[int])(nil)
// Queue 队列结构
type Queue[T comparable] struct {
list *arraylist.List[T]
}
// New 创建队列
func New[T comparable]() *Queue[T] {
return &Queue[T]{list: arraylist.New[T]()}
}
// Enqueue 入队
func (q *Queue[T]) Enqueue(value T) {
q.list.Add(value)
}
// Dequeue 出队
func (q *Queue[T]) Dequeue() (value T, ok bool) {
value, ok = q.list.Get(0)
if ok {
q.list.Remove(0)
}
return
}
// Peek 队头元素
func (q *Queue[T]) Peek() (value T, ok bool) {
return q.list.Get(0)
}
// Empty 判断队列为空
func (q *Queue[T]) Empty() bool {
return q.list.Empty()
}
// Size 获取队列元素个数
func (q *Queue[T]) Size() int {
return q.list.Size()
}
// Clear 清空队列
func (q *Queue[T]) Clear() {
q.list.Clear()
}
// Values 获取队列元素切片
func (q *Queue[T]) Values() []T {
return q.list.Values()
}
// String 字符串显示队列
func (q *Queue[T]) String() string {
str := "ArrayQueue: "
values := []string{}
for _, value := range q.list.Values() {
values = append(values, fmt.Sprintf("%v", value))
}
str += "[" + strings.Join(values, ", ") + "]"
return str
}
参考:https://blog.csdn.net/u014374975/article/details/133905842