go语言的一点优化

文章介绍了如何使用Go语言进行代码优化,包括使用strings.Builder优化字符串拼接,避免不必要的内存分配,以及利用sync.Pool管理内存池,提高性能。示例涵盖了字符串反转和单词计数的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近跟着青训营,学了go的优化,虽然不是很懂,以下是自己的一点笔记。

示例一 字符串反转

下面是一个示例的 Go 程序,用于将一个字符串中的每个单词进行反转:

package main

import (
	"fmt"
	"strings"
)

func reverseWords(s string) string {
	words := strings.Split(s, " ")
	for i, word := range words {
		words[i] = reverseString(word)
	}
	return strings.Join(words, " ")
}

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 main() {
	s := "Hello World! This is a sample string."
	result := reverseWords(s)
	fmt.Println(result)
}

这个程序使用了 strings.Split 和 strings.Join 函数来分割和拼接字符串,以及 reverseString 函数来反转每个单词。现在我们来对这份代码进行优化。

首先,我们可以使用 strings.Builder 来优化字符串的拼接过程,避免每次拼接都创建新的字符串对象:

func reverseWords(s string) string {
	words := strings.Split(s, " ")
	var builder strings.Builder
	for i, word := range words {
		words[i] = reverseString(word)
		builder.WriteString(words[i])
		builder.WriteString(" ")
	}
	return strings.TrimSpace(builder.String())
}

这里我们使用了 strings.Builder 来代替字符串拼接,通过 WriteString 方法将反转后的单词逐个写入到 builder 中,并在每个单词之间加上空格。最后通过 TrimSpace 函数去除首尾的空格。

接下来,我们可以对 reverseString 函数进行优化,避免使用 []rune 进行字符反转:

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)
}

这个优化可以避免每次都创建 []rune 的开销,而是直接在原字符串上进行字符交换。

最后,我们可以使用 strings.Builder 的 Grow 方法来预分配足够的内存空间,以减少内存分配和拷贝的次数:

func reverseWords(s string) string {
	words := strings.Split(s, " ")
	var builder strings.Builder
	builder.Grow(len(s))
	for i, word := range words {
		words[i] = reverseString(word)
		builder.WriteString(words[i])
		builder.WriteString(" ")
	}
	return strings.TrimSpace(builder.String())
}

通过调用 builder.Grow(len(s)),我们预分配了足够的内存空间,避免了字符串拼接过程中的内存分配和拷贝。

示例二 统计每个单词出现的次数

package main

import (
	"fmt"
	"strings"
)

func countWords(text string) map[string]int {
	words := strings.Fields(text)
	counts := make(map[string]int)
	for _, word := range words {
		counts[word]++
	}
	return counts
}

func main() {
	text := "Hello World! This is a sample text. Hello World!"
	counts := countWords(text)
	fmt.Println(counts)
}

这个程序使用了 strings.Fields 函数将文本分割成单词,并使用 map[string]int 来统计每个单词出现的次数。现在我们来对这份代码进行优化。

首先,我们可以使用空结构体 struct{} 作为 map 的值类型,来节省内存占用:

func countWords(text string) map[string]struct{} {
	words := strings.Fields(text)
	counts := make(map[string]struct{})
	for _, word := range words {
		counts[word] = struct{}{}
	}
	return counts
}

这里我们将 map[string]int 改为 map[string]struct{},并将 counts[word]++ 改为 counts[word] = struct{}{}。这样做的好处是,空结构体不占用任何内存空间,只需要一个零字节的占位符,可以节省大量的内存。

接下来,我们可以使用 sync.Pool 来管理 map 对象的内存池(当然也用了map预分配内存),避免频繁的内存分配和垃圾回收。(sync.Pool 是一个线程安全的对象池,它维护了一个存放对象的池子。)

var countsPool = sync.Pool{
	New: func() interface{} {
		return make(map[string]struct{})
	},
}

func countWords(text string) map[string]struct{} {
	words := strings.Fields(text)
	counts := make(map[string]struct{}, len(words))
	for _, word := range words {
		counts[word] = struct{}{}
	}
	return counts
}

这里我们通过 sync.Pool 创建了一个 map 对象的内存池,通过 New 方法来创建新的 map 对象。在 countWords 函数中,我们通过 countsPool.Get() 来获取一个空闲的 map 对象,然后进行统计操作。最后通过 defer countsPool.Put(counts) 将 map 对象放回内存池。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值