1、利用 Map 实现去重与 set 的功能
1.1 实现set
golang 原生没有实现set方法,因此我们可以利用 map 的 key 唯一性的问题处理
package main var set = map[string]bool { } func main() { ... url := xxx if set[url] { // 表示集合中已经存在 return } set[url] = true // 否则如果不存在,设置为true } // 完成后,set的所有的key值为不重复的值
map的value值是布尔型,这会导致set多占用内存空间,解决这个问题,则可以将其替换为空结构。在Go中,空结构通常不使用任何内存。
unsafe.Sizeof(struct{}{}) // 结果为 0
优化后,如下:
type void struct{} var member void set := make(map[string]void) // New empty set set["Foo"] = member // Add for k := range set { // Loop fmt.Println(k) } delete(set, "Foo") // Delete size := len(set) // Size _, exists := set["Foo"] // Membership
1.2 判断key是否存在map
//存在 if _, ok := map[key]; ok { } //不存在 if _, ok := map[key]; !ok { }
2、数组的基本操作
2.1 创建、初始化数组
package main import "fmt" //数组初始化的各种方式 func arraySliceTest() { //创建数组(声明长度) 默认初始化 var array0 = [5]int{} fmt.Printf("array0--- type:%T \n", array0) rangeIntPrint(array0[:]) //创建数组(声明长度) var array1 = [5]int{1, 2, 3} fmt.Printf("array1--- type:%T \n", array1) rangeIntPrint(array1[:]) //创建数组(不声明长度) var array2 = [...]int{6, 7, 8} fmt.Printf("array2--- type:%T \n", array2) rangeIntPrint(array2[:]) //创建数组切片 var array3 = []int{9, 10, 11, 12} fmt.Printf("array3--- type:%T \n", array3) rangeIntPrint(array3) //创建数组(声明长度),并仅初始化其中的部分元素 var array4 = [5]string{3: "Chris", 4: "Ron"} fmt.Printf("array4--- type:%T \n", array4) rangeObjPrint(array4[:]) //创建数组(不声明长度),并仅初始化其中的部分元素,数组的长度将根据初始化的元素确定 var array5 = [...]string{3: "Tom", 2: "Alice"} fmt.Printf("array5--- type:%T \n", array5) rangeObjPrint(array5[:]) //创建数组切片,并仅初始化其中的部分元素,数组切片的len将根据初始化的元素确定 var array6 = []string{4: "Smith", 2: "Alice"} fmt.Printf("array6--- type:%T \n", array6) rangeObjPrint(array6) } //输出整型数组切片 func rangeIntPrint(array []int) { for i, v := range array { fmt.Printf("index:%d value:%d\n", i, v) } } //输出字符串数组切片 func rangeObjPrint(array []string) { for i, v := range array { fmt.Printf("index:%d value:%s\n", i, v) } } func main() { arraySliceTest() }
2.2 求数组的长度
package main import "fmt" func main() { myArray := [3][4]int{{1,2,3,4},{1,2,3,4},{1,2,3,4}} //打印一维数组长度 fmt.Println(len(myArray)) //打印二维数组长度 fmt.Println(len(myArray[1])) mySlice := myArray[0] fmt.Println(mySlice) fmt.Println(len(mySlice)) }
2.3 数组的最大值
package main import ( "fmt" ) func main() { var intArr = [...]int{3,-4,93,8,12,29} maxVal := intArr[0] maxValIndex := 0 for i := 0; i < len(intArr); i++ { //从第二个元素开始循环比较,如果发现有更大的数,则交换 if maxVal < intArr[i] { maxVal = intArr[i] maxValIndex = i } } fmt.Printf("maxVal=%v maxValIndex=%v \n", maxVal, maxValIndex) }
3、排序
3.1 排序整数、浮点数和字符串切片
intList := [] int {2, 4, 3, 5, 7, 6, 9, 8, 1, 0} float8List := [] float64 {4.2, 5.9, 12.3, 10.0, 50.4, 99.9, 31.4, 27.81828, 3.14} stringList := [] string {"a", "c", "b", "d", "f", "i", "z", "x", "w", "y"} sort.Ints(intList) sort.Float64s(float8List) sort.Strings(stringList)
3.2 使用自定义比较器排序
- 使用
sort.Slice
函数排序,它使用一个用户提供的函数来对序列进行排序,函数类型为func(i, j int) bool
,其中参数i
,j
是序列中的索引。 -
sort.SliceStable
在排序切片时会保留相等元素的原始顺序。
上面两个函数让我们可以排序结构体切片(order by struct field value)。
family := []struct { Name string Age int }{ {"Alice", 23}, {"David", 2}, {"Eve", 2}, {"Bob", 25}, } // 用 age 排序,年龄相等的元素保持原始顺序 sort.SliceStable(family, func(i, j int) bool { return family[i].Age < family[j].Age }) fmt.Println(family) // [{David 2} {Eve 2} {Alice 23} {Bob 25}]
3.3 排序任意数据结构
- 使用
sort.Sort
或者sort.Stable
函数。 - 他们可以排序实现了sort.Interface接口的任意类型
一个内置的排序算法需要知道三个东西:序列的长度,表示两个元素比较的结果,一种交换两个元素的方式;这就是sort.Interface的三个方法:
type Interface interface { Len() int Less(i, j int) bool // i, j 是元素的索引 Swap(i, j int) }
还是以上面的结构体切片为例子,我们为切片类型自定义一个类型名,然后在自定义的类型上实现 srot.Interface 接口
type Person struct { Name string Age int } // ByAge 通过对age排序实现了sort.Interface接口 type ByAge []Person func (a ByAge) Len() int { return len(a) } func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func main() { family := []Person{ {"David", 2}, {"Alice", 23}, {"Eve", 2}, {"Bob", 25}, } sort.Sort(ByAge(family)) fmt.Println(family) // [{David, 2} {Eve 2} {Alice 23} {Bob 25}] }
实现了sort.Interface的具体类型不一定是切片类型;下面的customSort是一个结构体类型。
type customSort struct { p []*Person less func(x, y *Person) bool } func (x customSort) Len() int {len(x.p)} func (x customSort) Less(i, j int) bool { return x.less(x.p[i], x.p[j]) } func (x customSort) Swap(i, j int) { x.p[i], x.p[j] = x.p[j], x.p[i] }
让我们定义一个根据多字段排序的函数,它主要的排序键是Age,Age 相同了再按 Name 进行倒序排序。下面是该排序的调用,其中这个排序使用了匿名排序函数:
sort.Sort(customSort{persons, func(x, y *Person) bool { if x.Age != y.Age { return x.Age < y.Age } if x.Name != y.Name { return x.Name > y.Name } return false }})
3.4 逆序
除了自定义函数来逆序,也有自带的逆序函数
package main import ( "fmt" "sort" ) func main(){ a:=[]int{3,7,9,4,5,6,2} // 正序1: sort.Ints(a) fmt.Println(a) // 正序2: sort.Slice(a, func(i, j int) bool { return a[i] < a[j] }) fmt.Println(a) // 倒序1: sort.Sort(sort.Reverse(sort.IntSlice(a))) fmt.Println(a) // 倒序2: sort.Slice(a, func(i, j int) bool { return a[i] > a[j] }) fmt.Println(a) var kArray = []string{"apt", "src", "fmt", "zoo", "amd", "yes"} // 正序1: sort.Strings(kArray) fmt.Println("正序:", kArray) // 正序2: sort.Slice(kArray, func(i, j int) bool { return kArray[i] < kArray[j] }) fmt.Println("逆序:", kArray) // 倒序1: sort.Slice(kArray, func(i, j int) bool { return kArray[i] > kArray[j] }) fmt.Println("逆序:", kArray) // 倒序2: sort.Sort(sort.Reverse(sort.StringSlice(kArray))) fmt.Println("逆序:", kArray) }