昨天的测试中,我以为Go的map实现是红黑树,所以在最先的Java代码使用了TreeMap。今天搞清楚了内幕,顿时觉得,索然无味。因为HashMap在数据插入的时候,很容易因为hash碰撞,引发扩容,从而导致内存复制。这些会影响代码本身的运行。因此换一个数据结构继续测试。
这次选择的是链表。这种数据结构很简单,双方玩不出什么花儿来。也不存在扩容的问题。Go和Java都有官方实现。更妙的是,链表内部每一个节点都是小结构体,相互有指针指向。因此向链表写入数据,需要频繁创建小对象。当相邻节点的所占据的内存空间不连续的时候,沿着节点顺序查找对象,实际上是内存随机查找。而在业务代码中,“频繁创建小对象”和“内存随机查找”,是很常见的运行时行为。
测试代码
代码的行为就是向双向链表的表头部分或表尾处插入数据。向表头部分加入数据的时候,会随机选择位置。
其中有一个重要的参数`randomRange`决定了表头中插入数据的索引范围。这个值越小,表头最前部分的越少范围会被顺序读取并写入数据。从而内存的随机查找范围越小。
Go
Go版本:go version go1.18.1 darwin/amd64
Java
Java版本:OpenJDK 64-Bit Server VM Temurin-17.0.3+7
C++
Clang版本:Apple clang version 13.1.6 (clang-1316.0.21.2.5)
编译:g++ -std=c++20 -O2 http://list_test.cc
测试结果
randomRange = 50时,
Go

Java

C++

randomRange = 500时,
Go

Java

C++

randomRange = 1000时,
Go

Java

C++

总的来看,C++ > Java > Go,没有例外。