Java 如何评估哈希表实现?(使用HashMap作为参考)

Java 如何评估哈希表实现?(使用HashMap作为参考),java,performance,memory-management,hashmap,hashtable,Java,Performance,Memory Management,Hashmap,Hashtable,问题: 我需要比较两个哈希表实现(基本上是HashMap和另一个),并得出合理的结论 我对100%的准确度不感兴趣,只是我的估计方向正确而已 我不仅对每个操作的差异感兴趣,而且主要对哈希表作为“整体”的差异感兴趣 我对速度没有严格的要求,因此如果另一个实现合理地较慢,我可以接受它,但我确实期望/要求内存使用更好(因为其中一个哈希表由基元表支持) 到目前为止我做了什么: 最初,我创建了自己的自定义“基准测试”,使用循环和许多调用来提示gc,以了解差异,但我在网上看到,使用标准工具更可靠/更合适

问题:

  • 我需要比较两个哈希表实现(基本上是
    HashMap
    和另一个),并得出合理的结论

  • 我对100%的准确度不感兴趣,只是我的估计方向正确而已

  • 我不仅对每个操作的差异感兴趣,而且主要对哈希表作为“整体”的差异感兴趣

  • 我对速度没有严格的要求,因此如果另一个实现合理地较慢,我可以接受它,但我确实期望/要求内存使用更好(因为其中一个哈希表由基元表支持)

到目前为止我做了什么:

最初,我创建了自己的自定义“基准测试”,使用循环和许多调用来提示gc,以了解差异,但我在网上看到,使用标准工具更可靠/更合适。
我的方法示例(MapInterface只是一个包装器,因此我可以在实现之间切换):

int[]键=新的int[10000000];
字符串[]值=新字符串[10000000];
对于(int i=0;i最大值){
最大值=时间;
}
运行+=时间;
map=null;
map=createNewHashMap();
hintsToGC();
}  
返回新的长[]{min,max,run};
}     
public void hintsToGC(){
对于(int i=0;i<20;++i){
系统输出打印(“.”);
gc();
试一试{
睡眠(100);
}捕获(中断异常e){
e、 printStackTrace();
}           
} 
}
私有HashMapInterface createNewHashMap(){
if(jdk){
返回新的jdkhashmapprapper();
}  
否则{
返回新的AlternativeHashMapWrapper();
}  
}  
公共类JDKHashMapWrapper实现HashMapInterface{
HashMap;
jdkhashmapprapper(){
hashMap=新的hashMap();
}  
公共字符串put(整型键、字符串值){
返回hashMap.put(键,值);
}  
//等
}
(我想测试
放置
获取
包含
和内存利用率)
我可以通过使用我的方法确定我可以得到合理的测量值吗?
如果不是,最合适的工具是什么?如何使用

更新:
-我还使用SecureRandom使用随机数(也就是~10M随机数)进行测试。
-当哈希表调整大小时,我打印哈希表的逻辑大小/实际表的大小,以获得负载因子

更新:
对于我的特殊情况,如果我对整数也感兴趣,那么我的方法会有哪些陷阱

在@dimo414评论后更新

至少哈希表作为一个“整体”是没有意义的

我指的是哈希表在运行时和内存消耗的各种负载下的行为

每个数据结构都是不同方法的折衷

我同意。我的折衷方案是对内存改进的可接受访问惩罚

您需要确定您对验证哪些功能感兴趣

1) put(键、值)
2) 获取(键、值)
3) 康纳斯基(钥匙)

4) 所有这些,当哈希表中有许多条目时,我只是做了一些类似的事情,最后使用了中的内置探查器。您可以获得有关CPU和内存使用情况的详细信息。我最初是在Eclipse中编写所有代码的,但是Netbeans有一个导入特性,用于引入Eclipse项目,并且它将所有代码都设置为无问题的,如果您的情况也是这样的话

关于计时,您还可以查看ApacheCommons中的类。这是一种更直观的跟踪目标操作时间的方法,例如:

StopWatch myMapTimer = new StopWatch();
HashMap<Integer, Integer> hashMap = new HashMap<>();

myMapTimer.start();
for (int i = 0; i < numElements; i++)
    hashMap.put(i, i);
myMapTimer.stop();

System.out.println(myMapTimer.getTime()); // time will be in milliseconds
StopWatch myMapTimer=新秒表();
HashMap HashMap=新的HashMap();
myMapTimer.start();
对于(int i=0;i
使用哈希表的一些关键考虑因素是“bucket”分配的大小、冲突解决策略和数据的形状。本质上,哈希表接受应用程序提供的密钥,然后将其哈希为小于或等于分配的桶数的值。当两个键值散列到同一个bucket时,实现必须解决冲突并返回正确的值。例如,可以为每个bucket设置一个排序的链表,然后搜索该链表

如果您的数据碰巧有很多冲突,那么您的性能将受到影响,因为哈希表实现将花费太多时间来解决冲突。另一方面,如果您有大量的存储桶,则会以牺牲内存为代价来解决冲突问题。此外,如果条目数量超过一定数量,Java的内置HashMap实现将“重新设置”——我认为这是一个值得避免的昂贵操作

自从你
StopWatch myMapTimer = new StopWatch();
HashMap<Integer, Integer> hashMap = new HashMap<>();

myMapTimer.start();
for (int i = 0; i < numElements; i++)
    hashMap.put(i, i);
myMapTimer.stop();

System.out.println(myMapTimer.getTime()); // time will be in milliseconds
-javaagent: [PATH_TO]/classmexer.jar
MemoryUtil.deepMemoryUsageOf(mapIamInterestedIn, VisibilityFilter.ALL)