MST

星途 面试题库

面试题:Go语言复杂场景下切片的高效自定义排序

现有一个复杂结构体 `Data`,它包含多个嵌套结构体字段,例如 `Info struct{ SubInfo1 int; SubInfo2 string }` 和一个时间字段 `Time time.Time`。给定 `[]Data` 切片,需求是先按 `Time` 字段升序排列,如果 `Time` 相同,则按 `Info.SubInfo1` 降序排列。请实现该自定义排序,分析实现过程中的性能优化点以及可能遇到的并发安全问题(假设该切片可能会在多协程中读写)。
40.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

实现自定义排序

package main

import (
    "sort"
    "time"
)

type Data struct {
    Info  struct {
        SubInfo1 int
        SubInfo2 string
    }
    Time time.Time
}

type ByTimeAndSubInfo1 []Data

func (a ByTimeAndSubInfo1) Len() int           { return len(a) }
func (a ByTimeAndSubInfo1) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByTimeAndSubInfo1) Less(i, j int) bool {
    if a[i].Time.Equal(a[j].Time) {
        return a[i].Info.SubInfo1 > a[j].Info.SubInfo1
    }
    return a[i].Time.Before(a[j].Time)
}

func main() {
    var dataSlice []Data
    // 填充数据
    sort.Sort(ByTimeAndSubInfo1(dataSlice))
}

性能优化点

  1. 减少比较次数:在 Less 方法中,通过先比较 Time 字段,只有在 Time 字段相等时才比较 Info.SubInfo1,减少不必要的比较。
  2. 避免不必要的内存分配:尽量在已有数据结构上进行操作,避免在排序过程中频繁分配新的内存。

并发安全问题及解决方案

  1. 并发安全问题:当该切片在多协程中读写时,可能会出现数据竞争。例如,一个协程在读取切片数据时,另一个协程可能正在对切片进行排序或修改数据,导致读取到不一致的数据。
  2. 解决方案
    • 互斥锁(Mutex):使用 sync.Mutex 来保护对切片的读写操作。在读取或修改切片前,先获取锁,操作完成后释放锁。
    var mu sync.Mutex
    var dataSlice []Data
    
    func readData() {
        mu.Lock()
        defer mu.Unlock()
        // 读取 dataSlice 操作
    }
    
    func writeData() {
        mu.Lock()
        defer mu.Unlock()
        // 修改 dataSlice 操作,如排序
    }
    
    • 读写锁(RWMutex):如果读操作远多于写操作,可以使用 sync.RWMutex。读操作时获取读锁,写操作时获取写锁。读锁可以被多个读操作同时持有,而写锁会排斥其他读锁和写锁。
    var rwmu sync.RWMutex
    var dataSlice []Data
    
    func readData() {
        rwmu.RLock()
        defer rwmu.RUnlock()
        // 读取 dataSlice 操作
    }
    
    func writeData() {
        rwmu.Lock()
        defer rwmu.Unlock()
        // 修改 dataSlice 操作,如排序
    }