假如我们要生成一个固定长度的随机字符串,包含大小写字母,没有数字,没有特殊字符串,那么我们怎么做呢?需要怎样优化,才会更简单,更高效?在最终的方案之前,我们看看最常见的写法是怎样的,然后是如何一步步演进到最终的高效率方案的。好吧,先看下最原始的方案。
常见做法(Runes)
func init() {
rand.Seed(time.Now().UnixNano())
}
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func RandStringRunes(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
这个实现比较简单,二十六字母(大小写),然后随机取数,获得随机字符串。
Bytes改进
我们在最开始的时候进行了假设,我们的随机字符串只包含大小写字母,这样的话,我们发现没有必要使用rune
类型存储,因为在Golang(Go语言)UTF-8编码下,英文字母和byte
字节是一对一的。byte
的本质是uint8
类型,而rune
本质是int32
类型。我们改进后的代码如下:
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
仔细看上面的代码,我们不光对rune
类型进行了改进,还把原来的letter
变量变成了常量,这样len(letterBytes)
也是一个常量,代码的效率将大大提升。
余数改进
我们前面的方案都是通过调用rand.Intn()
生成的随机字符,这个rand.Intn()
其实是委托调用的Rand.Intn()
,而Rand.Intn()
最终又是调用的Rand.Int31n()
实现。相比我们直接调用rand.Int63()
来说,rand.Intn()
要慢很多。
所以我们可以把rand.Intn()
换成rand.Int63()
来提高效率,为了不超过letterBytes
的索引范围,我们使用余数来保证。
func RandStringBytesRmndr(n int) string {