面试题答案
一键面试1. 通过预分配策略优化map内存占用的方法
在许多编程语言中,如Go语言,当已知map大概会存储多少键值对时,可以在创建map时使用make
函数进行预分配。例如在Go语言中:
// 预分配能存储1000个键值对的map
myMap := make(map[string]int, 1000)
这样做可以避免在添加元素时频繁地进行内存重新分配和扩容操作,从而优化内存占用和性能。
2. 预分配与不预分配在性能和内存占用上的区别
性能方面
- 预分配:由于减少了内存重新分配的次数,在插入大量元素时,预分配内存的map性能会更好。每次内存重新分配都涉及到内存的复制和调整,这会带来额外的开销。例如在下面这个Go语言的性能测试代码中:
package main
import (
"fmt"
"time"
)
func main() {
start := time.Now()
myMap := make(map[string]int, 1000000)
for i := 0; i < 1000000; i++ {
key := fmt.Sprintf("key%d", i)
myMap[key] = i
}
elapsed := time.Since(start)
fmt.Printf("预分配内存插入100万个元素耗时: %s\n", elapsed)
start = time.Now()
anotherMap := make(map[string]int)
for i := 0; i < 1000000; i++ {
key := fmt.Sprintf("key%d", i)
anotherMap[key] = i
}
elapsed = time.Since(start)
fmt.Printf("不预分配内存插入100万个元素耗时: %s\n", elapsed)
}
一般情况下,预分配内存的myMap
插入元素的耗时会明显少于不预分配内存的anotherMap
。
- 不预分配:在插入元素时,如果map的容量不足,会触发扩容操作。扩容操作会导致性能下降,因为它需要重新分配内存、复制旧数据到新的内存地址等操作。
内存占用方面
- 预分配:在初始阶段,预分配的map会占用相对较多的内存,因为它预先分配了足够容纳预计数量键值对的空间。但随着元素的插入,内存增长相对平稳,因为减少了扩容带来的内存浪费(扩容时可能会多分配一些空间以减少后续扩容频率)。
- 不预分配:初始内存占用较少,但随着元素的不断插入,每次扩容时可能会多分配一定比例的内存空间以应对未来的增长,这可能导致在某些阶段内存占用比预分配的情况要多,而且频繁的扩容可能导致内存碎片化,进一步影响内存的使用效率。