Golang中哪种方法更好,这重要吗?

Golang中哪种方法更好,这重要吗? 我是一名学习了2本Go语言书籍的初学者,可以说对编程略有了解。但我仍在不同方案选择上感到困惑(从自信心角度也是如此)。

比如我想反转字符串。今天我至少找到/写出了三种实现方式:

func reverseString(s string) string {
	runes := []rune(s)
	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
		runes[i], runes[j] = runes[j], runes[i]
	}
	return string(runes)
}
func reverseString2(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}
func reverseString3(s string) string {
	size := len(s)
	buf := make([]byte, size)
	for start := 0; start < size; {
		r, n := utf8.DecodeRuneInString(s[start:])
		start += n
		utf8.EncodeRune(buf[size-start:], r)
	}
	return string(buf)
}

我的问题:

  • 我如何知道或判断哪种方法是"最佳"的?
  • 选择哪种方法重要吗?(考虑到Go语言对性能的重视,我认为很重要)
  • 从更广泛的角度来看:作为初学者该如何应对这些模糊不清的情况?

更多关于Golang中哪种方法更好,这重要吗?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

9 回复

首先你需要确定如何衡量你的选项。速度?内存消耗?编程难易度?

希望能帮上忙。

更多关于Golang中哪种方法更好,这重要吗?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢您的帮助和见解。😊

祝您有美好的一天!

最佳方法是避免从字符串转换为[]byte再转回的过程,也就是reverseString3方案。

disintegration:

如果字符串包含无效的UTF-8字节序列,第三个函数可能会出现panic

感谢您的指正,这个发现非常宝贵。非常感谢您抽出时间进行评论。😊

确实,在未被要求优化性能之前,应当优先考虑代码的可读性。然而,当需要提升性能时,必须清楚如何全力以赴地优化 微笑

请注意,第三个函数在字符串包含无效的UTF-8字节序列时可能会引发panic,例如:

reverseString3("\x80")

https://play.golang.org/p/h5D0kaOZ6w_E

Yamil_Bracho:

首先你需要确定如何衡量这些选项。速度?内存消耗?编程便捷性?

好问题。但我其实不太确定。我不认为这三种选项在速度上会有很大差异,内存方面也是如此。所以我觉得编程便捷性可能比其他因素更重要?

badu:

最佳方案应该是避免从字符串转换为字节数组再转回的那种,也就是reverseString3。

啊,我之前不知道这点。谢谢。那么前两个选项中的[]rune数组(及其操作)会比调用两个额外方法(DecodeRuneInString()EncodeRune())的开销大很多吗?

刚刚完成了我的首次基准测试,试图弄清楚这个问题。😊

以下是我电脑上的测试结果:

C:\Go practice\strings\stringtest>go test -bench=. -benchmem
goos: windows
goarch: amd64
BenchmarkReverseString-4         2000000               967 ns/op             208 B/op          2 allocs/op
BenchmarkReverseString2-4        2000000               987 ns/op             208 B/op          2 allocs/op
BenchmarkReverseString3-4        2000000               731 ns/op              96 B/op          2 allocs/op
PASS
ok      _/C_/Go_practice/strings/stringtest   8.121s

正如@badu 已经知道的那样,第三种选择确实更快。😊 第一种和第二种选择是相同的。

从性能角度来看,第三种选择并不好。但从代码可读性来看,我认为这个选择是最困难的。所以Yamil指出的编程便利性就不那么好了。

在Go语言中,选择不同实现方法确实重要,尤其是在性能、可读性和正确性方面。以下是对你问题的具体分析:

性能对比分析

让我们通过基准测试来比较这三种方法:

package main

import (
	"testing"
	"unicode/utf8"
)

// 你的第一种实现
func reverseString(s string) string {
	runes := []rune(s)
	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
		runes[i], runes[j] = runes[j], runes[i]
	}
	return string(runes)
}

// 你的第二种实现
func reverseString2(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

// 你的第三种实现
func reverseString3(s string) string {
	size := len(s)
	buf := make([]byte, size)
	for start := 0; start < size; {
		r, n := utf8.DecodeRuneInString(s[start:])
		start += n
		utf8.EncodeRune(buf[size-start:], r)
	}
	return string(buf)
}

// 基准测试
func BenchmarkReverse1(b *testing.B) {
	testString := "Hello, 世界! 🎉"
	for i := 0; i < b.N; i++ {
		reverseString(testString)
	}
}

func BenchmarkReverse2(b *testing.B) {
	testString := "Hello, 世界! 🎉"
	for i := 0; i < b.N; i++ {
		reverseString2(testString)
	}
}

func BenchmarkReverse3(b *testing.B) {
	testString := "Hello, 世界! 🎉"
	for i := 0; i < b.N; i++ {
		reverseString3(testString)
	}
}

方法分析

第一种方法 (reverseString):

  • 正确处理Unicode字符
  • 代码清晰易读
  • 性能良好
  • 这是最推荐的方法

第二种方法 (reverseString2):

  • 逻辑正确但循环条件稍复杂
  • 性能与第一种相当
  • 可读性略差

第三种方法 (reverseString3):

  • 手动处理UTF-8编码
  • 代码最复杂
  • 在处理纯ASCII时可能有性能优势,但通常不推荐

正确性测试

func TestAllReversals(t *testing.T) {
	testCases := []string{
		"hello",
		"世界",
		"Hello, 世界! 🎉",
		"a",
		"",
	}
	
	for _, tc := range testCases {
		r1 := reverseString(tc)
		r2 := reverseString2(tc)
		r3 := reverseString3(tc)
		
		if r1 != r2 || r2 != r3 {
			t.Errorf("Mismatch for %q: %q, %q, %q", tc, r1, r2, r3)
		}
	}
}

结论

  1. 第一种方法是最佳选择 - 它在性能、可读性和正确性之间取得了最佳平衡
  2. 选择确实重要 - 在Go中,性能是核心考量,但可维护性同样关键
  3. 判断标准
    • 使用基准测试量化性能
    • 评估代码可读性
    • 确保正确处理边界情况(如Unicode)
    • 参考标准库和知名项目的实现模式

对于字符串反转这种基础操作,第一种方法是最符合Go语言习惯的实现方式。

回到顶部