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、

 

posted @ 2021-08-02 11:23  快乐的欧阳天美1114  阅读(42)  评论(0编辑  收藏  举报