Java之HashMap、Hashtable对比测试
Java 8
---
编写程序测试 HashMap、Hashtable 的一些功能,并对二者进行对比。来自博客园
截取 参考文档1 的相关内容:
JDK源码:初始容量、loadFactor是两个重要概念,影响到 扩容时的性能。来自博客园
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
// 构造函数 4个
public HashMap() {...}
public HashMap(int initialCapacity) {...} // 最高频
public HashMap(int initialCapacity, float loadFactor) {...}
public HashMap(Map<? extends K, ? extends V> m) {...}
}
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
// 构造函数 4个
public Hashtable() {...}
public Hashtable(int initialCapacity) {...} // 最高频
public Hashtable(int initialCapacity, float loadFactor) {...}
public Hashtable(Map<? extends K, ? extends V> t) {...}
}
测试程序:来自博客园
package aug;
import java.time.Duration;
import java.time.Instant;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
public class Test80201 {
// 输出信息使用
private static Consumer<Object> cs = System.out::println;
public static void main(String[] args) {
test1();
cs.accept("\n---------------\n");
test2();
}
public static void test1() {
// 1、插入null值
cs.accept("1、插入null值");
Map<String, Integer> map = new HashMap<>(8);
map.put("a", 1);
map.put("b", 2);
map.put("c", null);
map.put(null, null);
cs.accept("map=" + map);
cs.accept("ma.null=" + map.get(null));
map.put(null, 999);
cs.accept("map=" + map);
cs.accept("map.null=" + map.get(null));
cs.accept("");
Hashtable<String, Integer> table = new Hashtable<String, Integer>(8);
table.put("a", 111);
table.put("b", 222);
// 不能插入null
// table.put("c", null);
// 不能插入null
// table.put(null, 333);
cs.accept("table=" + table);
cs.accept("");
// 2、迭代器
cs.accept("2、迭代器");
Iterator<Entry<String, Integer>> iter = map.entrySet().iterator();
cs.accept("输出iter:");
while(iter.hasNext()) {
Entry<String, Integer> ent = iter.next();
cs.accept(ent);
// fail-fast
// java.util.ConcurrentModificationException
// map.remove(ent.getKey());
}
Set<Entry<String, Integer>> tset = table.entrySet();
Iterator<Entry<String, Integer>> iter2 = tset.iterator();
cs.accept("输出iter2:");
while(iter2.hasNext()) {
Entry<String, Integer> ent = iter2.next();
cs.accept(ent);
// fail-fast
// java.util.ConcurrentModificationException
// table.remove(ent.getKey());
}
cs.accept("");
cs.accept("map=" + map);
cs.accept("table=" + table);
cs.accept("");
cs.accept("talbe.keys:");
Enumeration<String> keys = table.keys();
while(keys.hasMoreElements()) {
String key = keys.nextElement();
cs.accept(key + "=" + table.get(key));
// 未发生异常,删除成功
// 若发生在其它线程呢?
table.remove(key);
}
cs.accept("");
cs.accept("map=" + map);
cs.accept("table=" + table);
}
/**
* 多线程测试
* @author ben
* @date 2021-08-02 10:31:25 CST
*/
public static void test2() {
ExecutorService es1 = Executors.newFixedThreadPool(4);
// 关注点:
// 写入数据的最终数量是否符合预期;写入数据的效率;
final int size = 4096;
cs.accept("3、两个线线程往HashMap中写数据");
final Map<String, Integer> map = new HashMap<>(size);
es1.submit(()->{
int end = size/2;
Instant inst1 = Instant.now();
for (int i=0; i<end; i++) {
map.put("a"+i, i);
}
Instant inst2 = Instant.now();
cs.accept("HashMap插入" + end + "个元素,耗时:" + Duration.between(inst1, inst2).toNanos() + "纳秒");
});
es1.submit(()->{
int end = size/2;
for (int i=0; i<end; i++) {
map.put("b"+i, i);
}
});
try {
// 睡眠5秒
Thread.sleep(5000);
} catch (InterruptedException e) {
// nothing
}
// 每次执行可能不同,测试的插入数量越多,误差越大
cs.accept("map.size=" + map.size());
cs.accept("4、两个线线程往Hashtable中写数据");
final Hashtable<String, Integer> table = new Hashtable<>(size);
es1.submit(()->{
int end = size/2;
Instant inst1 = Instant.now();
for (int i=0; i<end; i++) {
table.put("c"+i, i);
}
Instant inst2 = Instant.now();
cs.accept("Hashtable插入" + end + "个元素,耗时:" + Duration.between(inst1, inst2).toNanos() + "纳秒");
});
es1.submit(()->{
int end = size/2;
for (int i=0; i<end; i++) {
table.put("d"+i, i);
}
});
try {
// 睡眠5秒
Thread.sleep(5000);
} catch (InterruptedException e) {
// nothing
}
cs.accept("table.size=" + table.size());
es1.shutdown();
}
}
执行结果:
1、插入null值
map={null=null, a=1, b=2, c=null}
ma.null=null
map={null=999, a=1, b=2, c=null}
map.null=999
table={b=222, a=111}
2、迭代器
输出iter:
null=999
a=1
b=2
c=null
输出iter2:
b=222
a=111
map={null=999, a=1, b=2, c=null}
table={b=222, a=111}
talbe.keys:
b=222
a=111
map={null=999, a=1, b=2, c=null}
table={}
---------------
3、两个线线程往HashMap中写数据
HashMap插入2048个元素,耗时:2990900纳秒
map.size=3935
4、两个线线程往Hashtable中写数据
Hashtable插入2048个元素,耗时:13965500纳秒
table.size=4096
同步Map 1:更改map为 Collections.synchronizedMap(new HashMap<>(size));
只执行 test2() 的测试结果:
---------------
3、两个线线程往HashMap中写数据
HashMap插入2048个元素,耗时:4987100纳秒
map.size=4096
4、两个线线程往Hashtable中写数据
Hashtable插入2048个元素,耗时:10967600纳秒
table.size=4096
结果分析:
map也写入了符合预期的数据,性能 比 Hashtable 好很多。
同步Map 2:更改map为 new ConcurrentHashMap<String, Integer>(size);
只执行 test2() 的测试结果:
---------------
3、两个线线程往HashMap中写数据
HashMap插入2048个元素,耗时:1996600纳秒
map.size=4096
4、两个线线程往Hashtable中写数据
Hashtable插入2048个元素,耗时:8028700纳秒
table.size=4096
结果分析:
map也写入了符合预期的数据,性能 比 Hashtable 好很多。
参考文档
1、Java面试基础
2、