我有一个包必须订购Go类型,快速。
目前我使用reflect.Type
s,用Name()
获取他们的名字,并将名称命名为字符串:
if type1.Name() < type2.Name() { ...
但是,它使用字符串比较。它有效,但我正在寻找更快的解决方案。
这个比较的确切方式对我来说并不重要 - 只有我需要的,
直接将reflect.Type
变量与<
进行比较并不起作用,因为此操作未在Go中为reflect.Type
定义。
可以生成类型名称的散列为64位或128位整数,然后比较这些整数。这是一种可能性,但我正在寻找一种更快的解决方案。
另一种可能性是将reflect.Type
变量与它们的不安全指针进行比较(转换为int64
),但据我所知,Go中无法保证不安全指针地址在流程的生命周期中不会发生变化。因此,我将失去我的(1)要求。
答案 0 :(得分:2)
Type.Name()
不好使用Type.Name()
进行比较是一个非常糟糕的主意,因为有许多匿名类型Type.Name()
返回空字符串,例如[]int
,*int
等。由Type.Name()
返回的不包含包名称,因此例如time.Time
和foo.Time
的名称将为"Time"
,因此被视为相等。不再需要说了。查看Identify non builtin-types using reflect了解详情。
一种简单快捷的方法是为所有需要比较的类型分配一个整数(例如int
值),然后您不需要进行任何string
比较或散列,只需比较分配的int
值。
唯一的问题是如何分配和记住这些int
值。指定的值可能只是连续值,从1开始,然后是2,3等。此分配和查找可能是自动的(隐藏在我们眼前)。
基本上我们想要一个像这样的函数:
// Compare compares 2 types, returns a negative number
// if t1 < t2, a positive number if t1 > t2, and 0 otherwise.
func Compare(t1, t2 reflect.Type) int {
}
实施非常简单/紧凑:
var (
registry = map[reflect.Type]int{}
max int
)
func value(t reflect.Type) int {
v := registry[t]
if v == 0 {
max++
v, registry[t] = max, max
}
return v
}
// Compare compares 2 types, returns a negative number
// if t1 < t2, a positive number if t1 > t2, and 0 otherwise.
func Compare(t1, t2 reflect.Type) int {
v1, v2 := value(t1), value(t2)
return v1 - v2
}
测试它:
tstring := reflect.TypeOf("")
tint := reflect.TypeOf(int(1))
ttime := reflect.TypeOf(time.Time{})
fmt.Println(Compare(tstring, tstring)) // 0
fmt.Println(Compare(tint, ttime)) // -1, tint gets registered first
fmt.Println(Compare(ttime, tint)) // 1
fmt.Println(Compare(tint, tint)) // 0
fmt.Println(Compare(tstring, ttime)) // -2
fmt.Println(Compare(ttime, tstring)) // 2
输出(在Go Playground上尝试):
0
-1
1
0
-2
2
但有一个缺点:实施对于并发使用是不安全的(通过多个goroutines)。如果我们需要安全性来同时使用,则必须同步对registry
地图(以及max
变量)的访问:
var mux sync.RWMutex
func value(t reflect.Type) int {
mux.RLock()
v := registry[t]
mux.RUnlock()
if v == 0 {
mux.Lock()
max++
v, registry[t] = max, max
mux.Unlock()
}
return v
}
其余的没有变化。输出是一样的。在Go Playground上试试这个。
现在这个Compare()
意味着在引擎盖下使用锁。另外,为了获得指定的类型的整数值,我们必须索引地图。此映射中的键的类型是reflect.Type
,它是一种接口类型。 reflect.Type
的当前实现是*reflect.rtype
这是一个指针,所以它仍然足够快,因为只有一个指针被哈希,然后在一个桶中查找(内置映射是一个hashmap实现)。 p>
现在这个Compare()
意味着在引擎盖下使用锁定(以及索引地图),因此如果您真的寻求最快的解决方案,这仍然是一个不可接受的“负担”。如果获取为该类型分配的内部int
值,并且使用它,甚至可以直接与另一个类型的值进行比较,则可以轻松地在需要比较时始终使用这些值。
为此,您需要做的就是“发布”(导出)value()
功能:
func Value(t reflect.Type) int {
// ...
}
甚至可以删除Compare()
。因此,需要这种类型比较的库可以查询分配给类型的整数值,并且它们可以“缓存”该值以避免再次调用它(因为它在进程的生命周期内不会改变),并且它可以与此Value()
函数获得的其他值进行比较。
例如:
vstring := Value(reflect.TypeOf(""))
vint := Value(reflect.TypeOf(int(1)))
vtime := Value(reflect.TypeOf(time.Time{}))
fmt.Println(vstring - vstring) // 0
fmt.Println(vint - vtime) // -1, tint gets registered first
fmt.Println(vtime - vint) // 1
fmt.Println(vint - vint) // 0
fmt.Println(vstring - vtime) // -2
fmt.Println(vtime - vstring) // 2
在Go Playground上试试这个。