更好地比较切片或字节?

时间:2018-03-20 04:07:47

标签: go

我只是好奇这些方法中的哪一种更好(或者如果有一种更好的方法,那我就错过了)。我试图确定一个单词的第一个字母和最后一个字母是否相同,并且有两个明显的解决方案。

if word[:1] == word[len(word)-1:]

if word[0] == word[len(word)-1]

根据我的理解,第一个是拉动字符串的片段并进行字符串比较,而第二个是从任一端拉出字符并比较为字节。

我很好奇两者之间是否有性能差异,以及是否有任何"更好的"这样做的方法?

3 个答案:

答案 0 :(得分:8)

在Go中,string是UTF-8编码的。 UTF-8是一种可变长度编码。

package main

import "fmt"

func main() {
    word := "世界世"
    fmt.Println(word[:1] == word[len(word)-1:])
    fmt.Println(word[0] == word[len(word)-1])
}

输出:

false
false

如果你真的想要比较一个字节而不是一个字符,那么编译器应该尽可能精确。显然,比较一个字节,而不是一个切片。

BenchmarkSlice-4    200000000            7.55 ns/op
BenchmarkByte-4     2000000000           1.08 ns/op

package main

import "testing"

var word = "word"

func BenchmarkSlice(b *testing.B) {
    for i := 0; i < b.N; i++ {
        if word[:1] == word[len(word)-1:] {
        }
    }
}

func BenchmarkByte(b *testing.B) {
    for i := 0; i < b.N; i++ {
        if word[0] == word[len(word)-1] {
        }
    }
}

答案 1 :(得分:3)

如果您的意思是rune,请使用:

func eqRune(s string) bool {
    if s == "" {
        return false // or true if that makes more sense for the app
    }
    f, _ := utf8.DecodeRuneInString(s)  // 2nd return value is rune size. ignore it.
    l, _ := utf8.DecodeLastRuneInString(s) // 2nd return value is rune size. ignore it.
    if f != l {
        return false
    }
    if f == unicode.ReplacementChar {
        // First and last are invalid UTF-8. Fallback to 
        // comparing bytes.
        return s[0] == s[len(s)-1]
    }
    return true
}

如果您的意思是字节,请使用:

func eqByte(s string) bool {
    if s == "" {
        return false // or true if that makes more sense for the app
    }
    return s[0] == s[len(s)-1]
}

比较单个字节比比较字符串切片要快,如另一个答案中的基准测试所示。

playground example

答案 2 :(得分:0)

字符串是一个字节序列。如果您知道字符串仅包含ASCII字符,则您的方法有效。否则,您应该使用处理多字节字符而不是字符串索引的方法。您可以将其转换为符文切片以处理代码点或字符,如下所示:

    r := []rune(s)
    return r[0] == r[len(r) - 1]

您可以在the official Go Blog post on the subject中阅读有关字符串,字节切片,符文和代码点的更多信息。

要回答您的问题,您发布的两个索引表达式之间没有明显的性能差异。

这是一个可运行的例子:

package main

import "fmt"

func EndsMatch(s string) bool {
    r := []rune(s)
    return r[0] == r[len(r) - 1]
}

func main() {
    tests := []struct{
        s   string
        e   bool
    }{
        {"foo", false},
        {"eve", true},
        {"世界世", true},
    }
    for _, t := range tests {
        r := EndsMatch(t.s)
        if r != t.e {
            fmt.Printf("EndsMatch(%s) failed: expected %t, got %t\n", t.s, t.e, r)
        }
    }
}

什么都不打印。