Go 切片、映射常见使用场景

Go 切片、映射常见使用场景

Go映射分组,切片转换为映射

Go语言中切片转换为映射的分组方法

在Go语言中,可以使用map类型对切片进行分组操作。常见的做法是通过遍历切片,根据某个键(如字段、值或自定义逻辑)将元素归类到映射中。

go 复制代码
type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {Name: "Alice", Age: 25},
        {Name: "Bob", Age: 30},
        {Name: "Charlie", Age: 25},
        {Name: "Diana", Age: 30},
    }

    // 按年龄分组
    grouped := make(map[int][]Person)
    for _, p := range people {
        grouped[p.Age] = append(grouped[p.Age], p)
    }

    // 输出结果
    for age, group := range grouped {
        fmt.Printf("Age %d: %v\n", age, group)
    }
}

该方式利用map[int][]Person作为容器,将具有相同年龄的人员聚合在一起。适用于任意可比较类型的键。

有个注意点,Go 中 append 允许向零值nil切片添加元素

Go 复制代码
package main

import "fmt"

func main() {
	var s []string
	fmt.Println(s, len(s), cap(s))
	if s == nil {
		fmt.Println("nil!")
	}
	s = append(s, "append nil")
	fmt.Println(s)
}

运行结果

bash 复制代码
[] 0 0
nil!
[append nil]

使用函数式风格实现分组(更简洁)

借助高阶函数思想,可封装一个通用的分组函数:

go 复制代码
func GroupBy[T any, K comparable](slice []T, keyFunc func(T) K) map[K][]T {
    result := make(map[K][]T)
    for _, item := range slice {
        k := keyFunc(item)
        result[k] = append(result[k], item)
    }
    return result
}

// 使用示例
grouped := GroupBy(people, func(p Person) int { return p.Age })

此写法支持泛型,适用于任何类型和键提取逻辑。


与Java对比:集合分组操作

在Java中,Stream API 提供了强大的分组能力,尤其配合 Collectors.groupingBy() 方法。

java 复制代码
import java.util.*;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Charlie", 25),
            new Person("Diana", 30)
        );

        Map<Integer, List<Person>> grouped = people.stream()
            .collect(Collectors.groupingBy(Person::getAge));

        grouped.forEach((age, list) ->
            System.out.println("Age " + age + ": " + list)
        );
    }
}

Java的groupingBy支持嵌套分组、自定义分类器、收集器组合等高级功能。


关键差异对比

特性 Go Java
分组语法 手动循环 + map Stream + Collectors.groupingBy
类型系统 泛型支持有限(需手动定义) 强泛型支持,类型安全
可读性 需编写循环逻辑 一行表达式,声明式风格
性能 更低开销,无额外对象创建 略高,涉及中间流对象
代码复用 需自行封装函数 标准库提供丰富工具
复杂分组 需多层嵌套 map 支持二级分组、自定义收集器

实用建议

  • 在Go中,若频繁进行分组操作,建议封装通用的GroupBy函数。
  • 对于复杂分组逻辑(如按多个字段分组),可在Go中使用复合键(如struct{K1,K2})作为map的键。
  • Java在处理数据流分组时更具表达力,但Go更适合高性能、低延迟场景。

示例:复合键分组(Go)

go 复制代码
type Key struct {
    Age  int
    Name string
}

grouped := make(map[Key][]Person)
for _, p := range people {
    key := Key{Age: p.Age, Name: p.Name}
    grouped[key] = append(grouped[key], p)
}

虽然不常用,但可通过结构体实现多维分组。


总结

  • Go :强调显式控制与性能,分组依赖手动循环与map结构。
  • Java :以声明式编程为主,Stream+Collectors提供强大且易读的分组能力。
  • 两者各有优势,选择取决于项目需求、团队习惯与性能要求。

Go 使用指针传递切片以实现修改

切片作为函数参数时的引用传递

在 Go 语言中,切片是一个包含三个字段的结构体:指向底层数组的指针、长度(len)和容量(cap)。当将切片作为参数传递给函数时,传递的是该结构体的副本。因此,直接修改切片变量本身(如重新赋值 s = [...])不会影响调用方的原始切片。

使用指针传递切片以实现修改

为了在函数内部修改原切片的内容或其引用,必须将切片的指针传入函数。通过解引用指针并进行操作,可以改变原始切片的底层数组内容或其长度、容量等属性。

go 复制代码
package main

import "fmt"

func appendEle(s *[]string) {
    *s = append(*s, "test")
}

func main() {
    s := []string{"1"}
    appendEle(&s)
    fmt.Println(s)
}

输出结果

运行上述代码,输出为:

复制代码
[1 test]

关键点说明

  • appendEle(&s) 将切片 s 的地址传入函数,函数接收的是 *[]string 类型。
  • 函数内部通过 *s 解引用,获取原始切片的值,并使用 append 向其追加元素。
  • 因为底层数组是共享的,且 append 可能会触发扩容,但新切片仍指向同一底层数组(除非容量不足),所以修改生效。
  • 调用后 main 中的 s 已被更新,包含新增元素 "test"。

注意事项

  • 若仅传递切片副本(如 appendEle(s)),则无法修改原始切片。
  • 使用指针传递切片是 Go 中常见模式,用于需要修改切片本身(如增删元素、改变长度)的场景。
  • 不建议对切片本身进行重新赋值时忽略指针,否则更改不会反映到调用者。

Go 传递映射时实现修改

在 Go 语言中,映射(map)是一种引用类型。当将映射作为参数传递给函数时,传递的是映射的底层数据结构的引用,而非副本。这意味着函数内部对映射的修改会直接影响原始映射。

go 复制代码
package main

import "fmt"

func modifyMap(s map[string]int) {
    s["s"] = 1
}

func main() {
    s := make(map[string]int)
    s["a"] = 0
    modifyMap(s)
    fmt.Println(s)
}

运行上述代码,输出为:

复制代码
map[a:0 s:1]

说明

  • make(map[string]int) 创建了一个空的映射。
  • s["a"] = 0 向映射中添加一个键值对。
  • modifyMap(s) 将映射 s 传入函数,函数内部通过 s["s"] = 1 修改了映射内容。
  • 由于映射是引用类型,函数内对 s 的修改作用于原始映射,因此主函数中的 s 被更新。

关键点总结

  • 映射在 Go 中是引用类型,传递时不复制整个数据结构。
  • 函数内对映射的增删改操作会反映到调用方的原始映射上。
  • 不需要使用指针来实现映射的修改,直接传递即可生效。
相关推荐
mldong1 小时前
工作流引擎的退回修改:金额从 6666 改成 666,重提还是回到退回它的那个人
后端·架构
陈随易1 小时前
一个网吧网管,做了个网页,卖了 5000 万
前端·后端·程序员
dialect_9 小时前
KMP算法
开发语言·c++·算法
总要学点什么10 小时前
第89篇:三维特效-螺旋圆效果——让螺旋臂卷起一圈圈外扩的涟漪
开发语言·javascript·ecmascript·webgl·cesium·空间分析·1024程序员节
深蓝AI10 小时前
Python 3.13 的 Queue.shutdown:异步消费者终于能正常收工了
后端
武子康11 小时前
MultiNet6 空闲崩溃:`EXCVADDR=0x8` 后又出现 `CORRUPT HEAP`
人工智能·后端·agent
开开心心_Every11 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
chenbingjie_c12 小时前
C++ 多态完全指南:从虚函数重写到虚函数表原理
开发语言·c++
吴卫斌12 小时前
定投ETF实战指南(四):行业/主题ETF——定投的“进攻端“
开发语言·python·股票·量化