企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
> # ​数组和切片的区别 - 大小和长度 - 数组:数组是固定长度的。在声明时指定长度,并且不可更改。 - 切片:切片底层引用一个数组,可以动态扩容。切片的长度和容量可以在运行时调整。 - 传递方式 - 数组:数组是值传递,会拷贝一个副本。修改其中一个副本的值不会影响另一个(赋值和函数传参时都会复制整个数组的数据)。 - 切片:切片是引用类型,传递时会传递底层数组的引用。多个切片可以共享同一个底层数组,因此修改一个切片的元素可能会影响其他切片。 - 内存分配 - 数组:数组在定义时立即分配固定大小的内存。 - 切片:未初始化的切片(即nil切片)不占用内存。当切片被初始化(使用`make`或赋值时)时,才会分配内存。 - 什么时候用数组 - 固定元素个数,不需要对数据进行增删操作 - 切片的底层 - ~~~ // runtime/slice.go 包含三个字段:长度、容量、底层数组 // 切片的扩容策略可以查看 runtime/slice.go growslice函数 type slice struct { array unsafe.Pointer len int cap int } ~~~ > # make 和 new 的区别 - new:用于任何类型,分配空间后是将内存清零没有初始化内存,返回类型T的指针 - make: 用于 slice, map,channel 初始化和分配内存 > # append ~~~ package main import ( "fmt" ) func main() { //一道关于 json 和 slice 的题:https://polarisxu.studygolang.com/posts/go/action/interview-slice-json/ s1 := []int{1, 2, 3} s2 := s1 fmt.Println(s2) //输出:[1 2 3] s1 = s1[:0] //len=0 cap=3 s1 = append(s1, 4) s1 = append(s1, 5) s1 = append(s1, 6) s1 = append(s1, 7) //发生扩容, 指向新的底层数据, 后续不会影响s1的值 s1 = append(s1, 8) fmt.Println(s2) //输出:[4 5 6] s1[0] = 9 fmt.Println(s2) //输出:[4 5 6] } ~~~ > # copy 是浅拷贝 or 深拷贝 - 对切片是值类型的, copy没区别 ~~~ package main import "fmt" func main() { s1 := []int{1, 2, 3, 4, 5} s2 := make([]int, len(s1), len(s1)) copy(s2, s1) s1[0] = 2 //当切片的元素类型是引用类型(如映射、切片、指针、通道等)时,copy 函数会复制这些引用,而不是它们引用的值 s3 := []map[string]int{{"A": 1}, {"B": 2}, {"C": 3}, {"D": 4}, {"E": 5}} s4 := make([]map[string]int, len(s3), len(s3)) copy(s4, s3) s3[0]["A"] = 6 fmt.Println("S1:", s1) fmt.Println("S2:", s2) fmt.Println("S3:", s3) //S3: [map[A:6] map[B:2] map[C:3] map[D:4] map[E:5]] fmt.Println("S4:", s4) //S4: [map[A:6] map[B:2] map[C:3] map[D:4] map[E:5]] } ~~~ - 深拷贝方式 ~~~ package main import "fmt" func main() { s3 := []map[string]int{{"A": 1}, {"B": 2}, {"C": 3}, {"D": 4}, {"E": 5}} s4 := DeepCopySliceOfMaps(s3) s3[0]["A"] = 6 fmt.Println("S3:", s3) //S3: [map[A:6] map[B:2] map[C:3] map[D:4] map[E:5]] fmt.Println("S4:", s4) //S3: [map[A:1] map[B:2] map[C:3] map[D:4] map[E:5]] } func DeepCopySliceOfMaps(slice []map[string]int) []map[string]int { newSlice := make([]map[string]int, len(slice)) for i, _ := range slice { newMap := make(map[string]int) for k, _ := range slice[i] { newMap[k] = slice[i][k] } newSlice[i] = newMap } return newSlice } ~~~ > # 左闭右开 ~~~ package main import "fmt" func main() { s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 取第三个到第五个元素(注意:索引是从0开始的) fmt.Println(s[2:5]) // 输出: [3 4 5] } ~~~ > # make([]int, 5) 和 make([]int, 0, 5) 的区别 ~~~ package main import "fmt" func main() { // make([]int, 5) 和 make([]int, 0, 5) 的区别 s1 := make([]int, 5) s2 := make([]int, 0, 5) fmt.Println(s1) // [0 0 0 0 0] fmt.Println(s2) // [] fmt.Println(s1[2]) // 0 //fmt.Println(s2[2]) // 报错:index out of range [2] with length 0 s3 := s1[2:5] s4 := s2[2:5] //索引操作: s2[2] 报错,必须在有效长度内。切片操作 s2[2:5] 只要索引在容量范围内就不会报错 fmt.Println(s3, len(s3), cap(s3)) //[0 0 0] 3 3 fmt.Println(s4, len(s4), cap(s4)) //[0 0 0] 3 3 s5 := []int{1, 2} copy(s1, s5) copy(s2, s5) fmt.Println(s1) // [1 2 0 0 0] fmt.Println(s2) // [] // 尝试将 s5 中的元素复制到 s2。由于 s2 的长度为 0,实际并不会复制任何元素。 s1 = append(s1, 3) s2 = append(s2, 3) fmt.Println(s1) // [1 2 0 0 0 3] fmt.Println(s2) // [3] var s6 []int s7 := make([]int, 5) s8 := make([]int, 0, 5) fmt.Println(s6[:]) //[] s6 = append(s6, 1) //nil 切片也可以 append s7 = append(s7, 1) s8 = append(s8, 1) fmt.Println(s6) //[1] fmt.Println(s7) //[0 0 0 0 0 1] fmt.Println(s8) //[1] } ~~~ > # 切片的下标 ~~~ package main import ( "fmt" ) func main() { // 像 `'a'` 这样的字符(`rune`, `byte` 类型)是可以作为切片的索引的,但它会被解释为对应的 **整数值**。 s := []int{0: 1, 3: 5, 'a': 6} k1 := rune('a') k2 := byte('a') fmt.Println(s[k1]) fmt.Println(s[k2]) fmt.Println(s, len(s), cap(s)) //len=98 cap=98 } ~~~ > # 遍历 []struct{} 使用下标而不是 range ~~~ package main type MyStruct struct { Field1 int Field2 string } func main() { slice := []MyStruct{ {1, "one"}, {2, "two"}, {3, "three"}, } for i := 0; i < len(slice); i++ { // 直接通过下标访问元素,没有发生副本拷贝 _ = slice[i] // 这里不会拷贝结构体 } // 遍历时不发生拷贝 for key := range slice { // 直接通过索引访问元素,没有发生副本拷贝 _ = slice[key] } for _, item := range slice { // item 是 slice 中元素的副本,发生了拷贝 _ = item // 这里发生了结构体的副本拷贝 } } ~~~ > # 删除某个元素 ~~~ package main import "fmt" func main() { s := []int{1, 2, 3, 4, 5} fmt.Println(RemoveElementByIndex(s, 3)) } // 使用切片切割:如果您知道要删除的元素的索引,可以直接使用切片切割操作来删除元素 func RemoveElementByIndex(s []int, index int) []int { if index >= 0 && index < len(s) { s = append(s[0:index-1], s[index:len(s)]...) } return s } ~~~ > # 多字段排序 ~~~ package main import ( "encoding/json" "fmt" "reflect" "sort" ) func main() { s := []map[string]interface{}{ {"symbol": "000005", "open_price": 102, "close_price": 202}, {"symbol": "000001", "open_price": 100, "close_price": 200}, {"symbol": "000002", "open_price": 101, "close_price": 201}, {"symbol": "000002", "open_price": 103, "close_price": 203}, {"symbol": "000001", "open_price": 104, "close_price": 204}, {"symbol": "000001", "open_price": 105, "close_price": 205}, {"symbol": "000005", "open_price": 106, "close_price": 206}, } fields := []SortField{ {Key: "symbol", IsAsc: true}, {Key: "open_price", IsAsc: false}, } SortByKey(s, fields) fmt.Println(s) } type SortField struct { Key string `json:"key"` IsAsc bool `json:"is_asc"` } func SortByKey(slice []map[string]interface{}, fields []SortField) { sort.SliceStable(slice, func(i, j int) bool { for k, _ := range fields { key := fields[k].Key isAsc := fields[k].IsAsc switch v1 := slice[i][key].(type) { case int: v2, _ := slice[j][key].(int) if v1 != v2 { if isAsc { return v1 < v2 } else { return v1 > v2 } } case int32: v2, _ := slice[j][key].(int32) if v1 != v2 { if isAsc { return v1 < v2 } else { return v1 > v2 } } case int64: v2, _ := slice[j][key].(int64) if v1 != v2 { if isAsc { return v1 < v2 } else { return v1 > v2 } } case float32: v2, _ := slice[j][key].(float32) if v1 != v2 { if isAsc { return v1 < v2 } else { return v1 > v2 } } case float64: v2, _ := slice[j][key].(float64) if v1 != v2 { if isAsc { return v1 < v2 } else { return v1 > v2 } } case string: v2, _ := slice[j][key].(string) if v1 != v2 { if isAsc { return v1 < v2 } else { return v1 > v2 } } case json.Number: v2, _ := slice[j][key].(json.Number) v1Float, err1 := v1.Float64() v2Float, err2 := v2.Float64() if err1 == nil && err2 == nil { if v1Float != v2Float { if isAsc { return v1Float < v2Float } else { return v1Float > v2Float } } } default: fmt.Printf("unsupported type for key '%s': %s\n", key, reflect.TypeOf(v1)) return false } } return false }) } ~~~ > # 按某个字段分组 ~~~ package main import ( "encoding/json" "fmt" ) func main() { s := []map[string]interface{}{ {"symbol": "000005", "open_price": 102, "close_price": 202}, {"symbol": "000001", "open_price": 100, "close_price": 200}, {"symbol": "000002", "open_price": 101, "close_price": 201}, {"symbol": "000002", "open_price": 103, "close_price": 203}, {"symbol": "000001", "open_price": 104, "close_price": 204}, {"symbol": "000001", "open_price": 105, "close_price": 205}, {"symbol": "000005", "open_price": 106, "close_price": 206}, } m := GroupByKey(s, "symbol") result, _ := json.Marshal(m) fmt.Println(string(result)) } func GroupByKey(slice []map[string]interface{}, key string) map[string][]interface{} { transformedData := make(map[string][]interface{}) for k, _ := range slice { val := fmt.Sprintf("%v", slice[k][key]) if data, ok := transformedData[val]; ok { transformedData[val] = append(data, slice[k]) } else { transformedData[val] = []interface{}{slice[k]} } } return transformedData } ~~~