登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go slices.Collect 如何把迭代器结果收集成切片

来源:17golang原创

时间:2026-09-12 16:40:27 432浏览 收藏

在 Go 1.23 以后,如果手里已经有一个 iter.Seq[E],想把它变成普通切片,优先记住这一句:result := slices.Collect(seq)。它会消费序列并返回一个新的 []E;序列为空时结果是 nil。如果序列长度事先已知,而且这段代码在高频路径上,则可以自己 make 一个带容量的切片再 append,把容量策略掌握在调用方手里。

要点速览
  • slices.Collect 是 Go 1.23 新增的迭代器收集函数,参数类型是 iter.Seq[E]
  • 它返回新的切片头和底层存储;但元素本身仍按 Go 的赋值规则处理,不会递归深拷贝指针指向的对象。
  • 不知道序列长度时用 Collect 最省心;知道数量且关注扩容时,用预分配切片手动收集。

从切片或自定义序列开始

slices.Values 可以把已有切片暴露成只产生元素的 iter.Seq。这正好适合演示 Collect 的边界:输入仍然是原切片,输出是另一份切片。

package main

import (
	"fmt"
	"slices"
)

func main() {
	// Values 只提供迭代视图,不在这里创建结果切片。
	source := []string{"Go", "iterator", "collect"}
	result := slices.Collect(slices.Values(source))

	// 修改结果的切片槽位,不会改写 source 的对应元素。
	result[0] = "Golang"
	fmt.Println(source)
	fmt.Println(result)
}
Go slices.Values、iter.Seq 与 slices.Collect 之间的序列和新切片静态关系框图
图1:从原切片得到迭代序列,再由 slices.Collect 形成结果切片;这是静态技术框图,不是运行截图。

如果数据不是现成切片,也可以自己返回一个序列。迭代器通过 yield 把值交给调用方;一旦 yield 返回 false,生产者应停止继续提供数据。

slices.Collect 到底收集了什么

Collect 的签名是 func Collect[E any](seq iter.Seq[E]) []E。它解决的是“把 push 风格的序列落成切片”,不是排序、去重或深拷贝。下面这个例子同时展示自定义序列和空序列的结果:

package main

import (
	"fmt"
	"iter"
	"slices"
)

func evenNumbers() iter.Seq[int] {
	return func(yield func(int) bool) {
		for n := 0; n 

这里的 values 是普通的 []int,可以交给需要切片的旧接口;emptynil,所以不要只用长度判断“有没有分配过”,若业务区分 nil 与空切片,应把这个约定写进接口说明。

长度已知时,为什么还要手动预分配

Collect 不要求序列提供长度提示,因此调用方无法通过它直接声明“结果一定有 N 个元素”。内部收集需要随着元素到达追加,切片容量可能按运行时策略增长。多数业务代码不需要关心这个细节;但批量转换、热循环或大结果集里,已知上限时可以显式预留:

func collectWithCapacity(seq iter.Seq[string], size int) []string {
	// 只预留容量,长度从零开始,避免把未填充槽位当成结果。
	result := make([]string, 0, size)
	for value := range seq {
		// append 按实际产出数量增长长度;size 只是容量提示。
		result = append(result, value)
	}
	return result
}
场景建议要记住的代价
序列长度未知slices.Collect(seq)代码短,容量交给运行时增长
数量有可靠上限make([]E, 0, n)append上限估错会多占容量或仍需扩容
元素含指针或引用按需复制元素指向的数据Collect 不是深拷贝
Go slices.Collect 与 make 加 append 的容量策略、元素复制和 nil 边界静态关系框图
图2:未知长度与已知容量两条收集路径的静态边界;这是关系示意图,不代表具体运行时容量数值。

落地时最容易忽略的三个边界

第一,Collect 只建立结果切片,不会把元素内部的指针对象复制一份;例如元素是 *Config,结果里的指针仍可能指向同一份 Config。第二,空序列的 nil 结果在 JSON、接口约定或比较逻辑里可能和 []E{} 不同,需要按 API 契约决定是否归一化。第三,Collect 是消费序列的动作,自定义序列里应在停止信号到达后释放或终止自己的生产逻辑,不能继续无条件生成数据。

相关问题

Collect 能不能直接把 map 变成切片?

不能直接把 map 作为 iter.Seq 传入。可以先用 maps.Keysmaps.Values 得到序列,再 Collect;map 遍历顺序仍不应当被当成稳定排序。

Collect 会自动排序吗?

不会。需要排序时使用 slices.Sorted(seq) 或先 Collect 再调用合适的排序函数,别把收集和排序的职责混在一起。

Go 1.22 项目能调用 slices.Collect 吗?

不能按 Go 1.22 的标准库假设直接使用;该 API 随 Go 1.23 加入。旧项目可以保留手写 for rangeappend,或先升级工具链并确认 go.mod 与 CI 环境一致。

因此,普通迭代器结果落地时选 slices.Collect,已知规模且在意分配时用预分配 append;真正需要深拷贝或稳定顺序,则另外明确实现,别把它们误认为 Collect 的默认行为。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>