昨天的测试中,我以为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,没有例外。