Java高效计数器

1. 新手级计数器
如果使用这一类别的计数器,那么代码大致如下所示:

String source = "my name is name me and your name is her first her";
String[] words = source.split(" ");
// 新手级计数器
public static void testNaive(String[] words){
	HashMap<String, Integer> counter = new HashMap<String, Integer>();
	for (String w : words) {
		if(counter.containsKey(w)){
			int oldValue = counter.get(w);
			counter.put(w, oldValue+1);
		} else {
			counter.put(w, 1);
		}
	}
}

在每次循环中,判断是否包含了相应的key,如果包含,那么值在原来的基础上加1,如果没有,那就设置为1.
此种方式简单又直接,但并不是很有效率。效率不高的原因如下:
1.1 当一个key存在时,containsKey() 和 get() 分别调用了一次,这意味着对map进行了两次查找。
1.2 因为 Integer 是不可变的,每次循环在增加计数值的时候将会创建一个新的对象.
2. 入门级计数器
那么我们自然需要使用一个可变的整数来避免创建太多个Integer对象.可变整数类可以如下面所示来定义:

// 可变Integer
public static final class MutableInteger{
	private int val;
	public MutableInteger(int val){
		this.val = val;
	}
	public int get(){
		return this.val;
	}
	public void set(int val){
		this.val = val;
	}
	// 为了方便打印
	public String toString() {
		return Integer.toString(val);
	}
}

那么计数器可以用如下的方式来改进:

// 入门级计数器
public static void testBetter(String[] words){
	HashMap<String, MutableInteger> counter = new HashMap<String, MutableInteger>();
	for (String w : words) {
		if(counter.containsKey(w)){
			MutableInteger oldValue = counter.get(w);
			oldValue.set(oldValue.get()+1);	// 因为是引用,所以减少了一次HashMap查找
		} else {
			counter.put(w, new MutableInteger(1));
		}
	}
}

因为不需要创建太多的Integer对象,看起来好了一些。然而,key存在的情况下,每次循环依然要进行两次查找.

3. 卓越级计数器
HashMap 的 put(key,value) 方法会返回key对应的当前value.了解这个特性,我们可以利用原有值来进行递增,并不需要多次的查找.

public static void testEfficient(String[] words){
	HashMap<String, MutableInteger> counter = new HashMap<String, MutableInteger>();
	for (String w : words) {
		MutableInteger initValue = new MutableInteger(1);
		// 利用 HashMap 的put方法弹出旧值的特性
		MutableInteger oldValue = counter.put(w, initValue);
		if(oldValue != null){
			initValue.set(oldValue.get() + 1);
		}
	}
}

4. 性能差异
为了测试这三种实现方式的性能,采用了下面的代码。先看看结果如何,性能测试分别执行了多次,对每一个数量级的测试,误差不算太大,所以取其中的一个结果排列如下:

10000000 次循环:
新手级计数器: 7726594902
入门级计数器: 6516014840
卓越级计数器: 5736574103


1000000 次循环:
新手级计数器: 777480106
入门级计数器: 642932000
卓越级计数器: 571867738


100000 次循环:
新手级计数器: 84323682
入门级计数器: 70176906
卓越级计数器: 61219664


10000 次循环:
新手级计数器: 13279550
入门级计数器: 7874100
卓越级计数器: 6460172


1000 次循环:
新手级计数器: 4542172
入门级计数器: 2933248
卓越级计数器: 992749


100 次循环:
新手级计数器: 3092325
入门级计数器: 1101695
卓越级计数器: 423942


10 次循环:
新手级计数器: 1993788
入门级计数器: 558150
卓越级计数器: 153156


1 次循环:
新手级计数器: 1625898
入门级计数器: 427494
卓越级计数器: 69473


从上面的输出可以看到,10000次的时候, 13:8:6 秒,相差很明显.特别是 新手级计数器和入门级计数器之间的比例,这说明创建对象是很耗资源的操作。
当然,次数更多的差距不明显的原因在于,触发了多次的GC垃圾回收,同时也证明了垃圾回收的代价确实很大。

 

转载自:https://blog.csdn.net/renfufei/article/details/14120775#commentsedit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值