一、反射

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 main

import (
"fmt"
"reflect"
)
type User struct {
Name string
Age int
}

func main() {
u := User{"张三", 56}
v := reflect.ValueOf(u)
fmt.Println(v)
}

/*output:
{张三 56}
*/

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 main

import (
"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)
}
/*output:
main.User
struct
*/

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 main

import (
"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)
}
/*output:
56
*/

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 main

import (
"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)
}

/*output:
{王五 24}
{李四 13}
{张三 56}
*/

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 main

import (
"fmt"
"sort"
)
type User struct {
Name string
Age int
}

//可以声明为[]*User传入指针
type Users []User
func (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 是为了实现自定义的大于比较规则。不比已知的intstring等类型,对于未知类型Golang也不知道如何比较大小

然后对所得到的反射列表排序

这里会根据数组的特点选择是快排还是堆排