一、反射 Go基础教程可看
Go 系列教程 — 34. 反射
反射
Go 语言反射的实现原理
这里记下用的部分
1.1 reflect.Value reflect.Value
可以表示一个任意类型的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "fmt" "reflect" ) type User struct { Name string Age int } func main () { u := User{"张三" , 56 } v := reflect.ValueOf(u) fmt.Println(v) }
1.2 reflect.Kind 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "fmt" "reflect" ) type User struct { Name string Age int } func main () { u := User{"张三" , 56 } t := reflect.TypeOf(u) k := t.Kind() fmt.Println(t) fmt.Println(k) }
1.3 反射字段 想要利用反射拿到结构体的字段,然后对其操作,需要这个字段是可访问的,也就是首字母大写
reflect.Indirect()
函数用于获取传入参数指向的值
1 2 3 4 5 6 func Indirect (v Value) Value { if v.Kind() != Ptr { return v } return v.Elem() }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport ( "fmt" "reflect" ) type User struct { Name string Age int } func main () { u := User{"张三" , 56 } indirect := reflect.Indirect(reflect.ValueOf(&u)).FieldByName("Age" ) fmt.Println(indirect) }
1 2 3 func Indirect (v Value) Value {}func (v Value) Elem () Value {}
这二个长的很像的兄弟有什么区别
*Elem returns the value that the interface v contains or that the pointer v points to. It panics if v’s Kind is not Interface or Ptr. It returns the zero Value if v is nil.*
*Indirect returns the value that v points to. If v is a nil pointer, Indirect returns a zero Value. If v is not a pointer, Indirect returns v.*
如果reflect.Value
是一个指针, 那么v.Elem()
等价于reflect.Indirect(v)
如果不是指针
如果是interface
,那么reflect.Indirect(v)
返回原值,而v.Elem()
返回interface
中包含的值
如果是其它值, v.Elem()
会panic
,而reflect.Indirect(v)
返回原值
二、排序 2.1 场景 给定特定字段字符串对结构体切片进行排序,如下
sortutil.DescByField
按照User结构体中的Age字段从大到小对切片排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport ( "fmt" "reflect" ) type User struct { Name string Age int } func main () { userSlice := []User{{"张三" , 56 }, {"李四" , 13 }, {"王五" , 24 }} sortutil.DescByField(userSlice, "Age" ) }
2.2 sort.Slice Go中有个sort.Slice
方法可以对结构体切片排序
1 2 3 sort.Slice(userSlice , func (i, j int ) bool { return userSlice [i].Age > userSlice [j].Age })
但是这样得配合switch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 userSlice := []User{{"张三" , 56 }, {"李四" , 13 }, {"王五" , 24 }} field := "Name" switch field{ case "Age" : sort.Slice(userSlice , func (i, j int ) bool { return userSlice [i].Age > userSlice [j].Age }) case "Name" : sort.Slice(userSlice , func (i, j int ) bool { return userSlice [i].Name > userSlice [j].Name }) } for _, user := range userSlice{ fmt.Println(user) }
2.2 SortWrapper sort - The Go Programming Language
使用SortWrapper
优雅地实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "fmt" "sort" ) type User struct { Name string Age int } type Users []Userfunc (s Users) Len () int { return len (s) }func (s Users) Swap (i, j int ) { s[i], s[j] = s[j], s[i] }type ByName struct { Users }func (u ByName) Less (i, j int ) bool { return u.Users[i].Name < u.Users[j].Name }type ByAge struct { Users }func (u ByAge) Less (i, j int ) bool { return u.Users[i].Age < u.Users[j].Age}func main () { userSlice := []User{{"张三" , 56 }, {"李四" , 13 }, {"王五" , 24 }} sort.Sort(ByAge{userSlice}) for _, user := range userSlice{ fmt.Println(user) } }
然后配上map
可以曲线救国
1 2 3 4 5 6 m := make (map [string ]sort.Interface) m["Age" ] = ByAge{userSlice} m["Name" ] = ByName{userSlice} field := "Age" sort.Sort(m[field])
2.3 反射 使用反射排序n多年前就有大佬实现了,只不过是停止更新了,不过还是可以用的,使用起来挺简单的,按照给的示例模仿就ok 不过缺点就是慢,大概在数据量在几万以上时会看出区别。
主要是因为涉及到内存分配以及后续的GC
以及reflect
实现里面有大量的枚举,也就是for循环,比如类型之类的。
patrickmn/sortutil
How to sort an array of struct by field?
拿2.1
那个代码来讲
1 sortutil.DescByField(userSlice, "Age" )
该方法主要是通过反射按照给定的字段名从给定的切片中拿到关于该字段的列表
然后判断类型
stringDescending
是为了实现自定义的大于比较规则。不比已知的int
,string
等类型,对于未知类型Golang也不知道如何比较大小
然后对所得到的反射列表排序
这里会根据数组的特点选择是快排还是堆排