一尘不染

将字符串的元素作为字节访问是否执行转换?

go

在Go中,要访问的元素string,我们可以编写:

str := "text"
for i, c := range str {
  // str[i] is of type byte
  // c is of type rune
}

访问时str[i],Go会执行从rune到的转换byte吗?我猜答案是肯定的,但我不确定。如果是这样,那么以下哪种方法在性能方面更好?一个人是否比另一个人更可取(例如,按照最佳实践)?

str := "large text"
for i := range str {
  // use str[i]
}

要么

str := "large text"
str2 := []byte(str)
for _, s := range str2 {
  // use s
}

阅读 195

收藏
2020-07-02

共1个答案

一尘不染

以下哪种方法在性能方面更好?

绝对不是这个。

str := "large text"
str2 := []byte(str)
for _, s := range str2 {
  // use s
}

字符串是不可变的。[]byte易变。这意味着[]byte(str)制作副本。因此,上面将复制整个字符串。我发现不知道何时复制字符串是大型字符串性能问题的主要根源。

如果str2永不更改,则编译器 可以 优化副本。因此,最好像上面这样写,以确保字节数组不会被更改。

str := "large text"
for _, s := range []byte(str) {
  // use s
}

这样一来,str2以后就不可能修改并破坏优化。

但这是一个坏主意,因为它将破坏任何多字节字符。见下文。


至于字节/符文转换,由于性能不相等,因此不考虑性能。c将是一个符文,str[i]将是一个字节。如果您的字符串包含多字节字符,则必须使用符文。

例如…

package main

import(
    "fmt"
)

func main() {
    str := "snow ☃ man"
    for i, c := range str {
        fmt.Printf("c:%c str[i]:%c\n", c, str[i])
    }
}

$ go run ~/tmp/test.go
c:s str[i]:s
c:n str[i]:n
c:o str[i]:o
c:w str[i]:w
c:  str[i]: 
c:☃ str[i]:â
c:  str[i]: 
c:m str[i]:m
c:a str[i]:a
c:n str[i]:n

请注意,使用str[i]会破坏多字节Unicode雪人,它仅包含多字节字符的第一个字节。

无论如何,没有性能差异,因为range str必须逐个字符地进行工作,而不是逐字节进行。

2020-07-02