非特定类型多切片取交集方法的思考

问题起因

由于缺乏泛型机制,所以公共方法的返回值不好处理。

这里贴一个自己的实现,有更好的改进方案欢迎留言。

具体实现1

func version1(params ...interface{}) []interface{} {
	cache := make(map[interface{}]int)
	for _, param := range params {
		switch reflect.TypeOf(param).Kind() {
		case reflect.Slice:
			slice := reflect.ValueOf(param)
			for i := 0; i < slice.Len(); i++ {
				eachItem := slice.Index(i).Interface()
				cache[eachItem]++
			}
		}
	}
	ans := make([]interface{}, 0)
	for key, val := range cache {
		if val == len(params) {
			ans = append(ans, key)
		}
	}
	return ans
}

优点

直接返回interface的切片,处理简单。

不足

使用起来不那么方便,需要额外的类型转换。

具体实现2

func version2(ans interface{}, params ...interface{}) {
	if reflect.TypeOf(ans).Kind() != reflect.Ptr ||
		reflect.TypeOf(ans).Elem().Kind() != reflect.Slice {
		panic(fmt.Sprintf("ans type error %T", ans))
	}
	cache := make(map[interface{}]int)
	for _, param := range params {
		switch reflect.TypeOf(param).Kind() {
		case reflect.Slice:
			slice := reflect.ValueOf(param)
			for i := 0; i < slice.Len(); i++ {
				eachItem := slice.Index(i).Interface()
				cache[eachItem]++
			}
		}
	}
	v := reflect.ValueOf(ans).Elem()
	var i int
	for key, val := range cache {
		if i >= v.Cap() {
			newcap := v.Cap() + v.Cap()/2
			if newcap < 4 {
				newcap = 4
			}
			newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
			reflect.Copy(newv, v)
			v.Set(newv)
			v.SetLen(newcap)
			v.SetCap(newcap)
		}
		if val == len(params) {
			v.Index(i).Set(reflect.ValueOf(key))
			i++
		}
	}
	v.Set(v.Slice(0, i))
}

优点

参考标准库中json解析部分的实现方式,将返回值指针当做参数的一部分,使用起来方便些。

不足

由于变参语法限制,返回值指针只能作为第一个参数,不太符合习惯。

具体实现3

func version3(params ...interface{}) {
	var ans interface{}
	cache := make(map[interface{}]int)
	for _, param := range params {
		switch reflect.TypeOf(param).Kind() {
		case reflect.Slice:
			slice := reflect.ValueOf(param)
			for i := 0; i < slice.Len(); i++ {
				eachItem := slice.Index(i).Interface()
				cache[eachItem]++
			}
		case reflect.Ptr:
			ans = param
		}
	}
	if ans == nil ||
		reflect.TypeOf(ans).Elem().Kind() != reflect.Slice {
		panic(fmt.Sprintf("ans type error %T", ans))
	}
	v := reflect.ValueOf(ans).Elem()
	var i int
	for key, val := range cache {
		if i >= v.Cap() {
			newcap := v.Cap() + v.Cap()/2
			if newcap < 4 {
				newcap = 4
			}
			newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
			reflect.Copy(newv, v)
			v.Set(newv)
			v.SetLen(newcap)
			v.SetCap(newcap)
		}
		if val == len(params)-1 {
			v.Index(i).Set(reflect.ValueOf(key))
			i++
		}
	}
	v.Set(v.Slice(0, i))
}

优点

改进了之前的问题,现在返回值可以在任意位置了。

测试代码

import (
	"fmt"
	"reflect"
)

func main() {
	fmt.Println(version1([]int{1, 2, 3, 4, 5}, []int{2, 3, 4}, []int{3}))

	var ans2 []int
	version2(&ans2, []int{1, 2, 3, 4, 5}, []int{2, 3, 4}, []int{3})
	fmt.Println(ans2)

	var ans3 []int
	version3(&ans3, []int{1, 2, 3, 4, 5}, []int{2, 3, 4}, []int{3})
	fmt.Println(ans3)
}

测试结果

[3]
[3]
[3]