如何使用Golang len获取长度_Golang字符串与集合长度说明

len函数只能用于数组、切片、字符串、map和channel;对nil切片或nil map调用合法且返回0;字符串len返回UTF-8字节数,非Unicode字符数;[]rune可获取真实字符数但有性能开销。

len 函数能直接获取哪些类型的长度

len 是 Go 内置函数,不是方法,只能用于编译期已知长度的类型:数组([5]int)、切片([]string)、字符串(string)、map(map[string]int)、channel(chan int)。它不能用于结构体、指针、函数或自定义类型(除非底层类型是上述之一)。

常见错误是试图对 nil 切片或 nil map 调用 len —— 实际上这完全合法:len(nil切片) 返回 0,len(nil map) 也返回 0。真正 panic 的是取值(如 m["k"])或遍历(for range nil map)。

  • len("你好") 返回 6(字节数,UTF-8 编码)
  • len([]rune("你好")) 返回 2(Unicode 码点数)
  • len([3]int{1,2,3}) 返回 3(数组长度固定)
  • len(map[int]string{}) 返回 0(空 map)

字符串 len 返回的是字节数,不是字符数

Go 字符串底层是只读字节序列,len(s) 永远返回 UTF-8 编码后的字节数。中文、emoji 等多字节字符会拉高这个值,但不反映“人眼看到的字符个数”。

如果业务需要按 Unicode 字符计数(比如截断显示、校验用户名最大 10 个汉字),必须转成 []rune

name := "Hello世界?"
fmt.Println(len(name))        // 输出: 13(H-e-l-l-o-世-界-? 各自 UTF-8 字节数之和)
fmt.Println(len([]rune(name))) // 输出: 9(5 ASCII + 2 汉字 + 1 emoji = 9 个 rune)

注意:[]rune(s) 会分配新底层数组,对超长字符串(如 MB 级日志)频繁调用有性能开销。

切片和数组的 len 行为差异

数组长度是类型的一部分,len 在编译期确定;切片长度是运行时属性,可变。

  • arr := [4]int{1,2,3,4}; len(arr) → 永远是 4,不可修改
  • sl := []int{1,2,3}; sl = sl[:2]; len(sl) → 变为 2
  • len(sl)cap(sl) 可能不同:切片可能只用了底层数组一部分

误把 cap

len 用会导致逻辑错误,比如循环写满整个容量而非当前长度:

data := make([]byte, 0, 1024)
data = append(data, 'a', 'b')
// 错误:for i := 0; i < cap(data); i++ { ... } —— 会访问未初始化内存
// 正确:for i := 0; i < len(data); i++ { ... }

map 和 channel 的 len 使用场景与陷阱

len(m) 返回当前键值对数量,len(ch) 返回当前缓冲区中待读取元素个数。两者都常用于条件判断,但要注意并发安全。

  • map 的 len 不是原子操作,多 goroutine 写入时,即使只读 len 也可能触发 panic(Go 1.19+ 对并发读写 map 的 panic 更敏感)
  • channel 的 len(ch) 是瞬时快照,无法替代 selectrange 做同步控制
  • 不要用 len(ch) == cap(ch) 判断是否满 —— 非缓冲 channel 的 cap 是 0,len 也是 0,永远相等

真正需要长度感知的并发场景,应配合 sync.Map、互斥锁或 channel 自身的阻塞语义来设计,而不是依赖 len 做轮询判断。

最容易被忽略的是:字符串的 len[]runelen 在语义上完全不同,而 Go 不提供类似 Python 的 len(s, encoding='utf8') 这种带选项的接口 —— 开发者必须自己决定用字节还是 rune,并承担转换成本。