Golang 基本操作

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,其中参数ij是序列中的索引。
  • 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)

}

 

上一篇:变量与数据类型


下一篇:Golang语法