今天学习Java入门之作《Thinking in Java》,看的非常过瘾。在看到第七章关于HashMap的实现案例中,有一点自己的思考。
一个小功能
作者在举例如何运用HashMap时,编写了一个查看随机数是否均匀排布的功能,具体实现是:
设置循环次数为10000,利用(int)Math.random()*20生成0-19范围内的10000个随机数
利用该随机数作为HashMap的key值,存入HashMap中,利用containsKey(k)函数判断,若已存在该key,则value+1;若不存在,则新建一个节点,设置value初始值为1
遍历打印该HashMap对象,查看均匀排布情况
总的来说功能不是很复杂,利用key的唯一性来确保新生成的随机数总是能被统计到,利用value值来统计生成的随机数个数。
书中源码如下:
public static class Counter{
public Counter(){};
int i = 1;
public String toString(){
return Integer.toString(i);
}
}
public static void main(String[] args) {
HashMap<Integer,Counter> hm = new HashMap<>();
for(int i = 0;i < 10000;i++){
Integer k = (int)(Math.random()*20);
if(hm.containsKey(k)){
hm.get(k).i++;
}else{
hm.put(k, new Counter());
}
}
System.out.println(hm);
}
作者提出的问题
这里作者回答了为什么要新建Counter类来进行value存储的问题:
(详见原著P346)
你可能会猜想class Counter的必要性,看起来它似乎连外覆盖类Integer的功能都不具备。为什么不使用int或Integer呢?你不能使用int,因为所有容器都只能持有Object的reference。
……
不过,使用Java外覆盖类时,你只能在初始化时设定其值。换句话说你不能在喊声外覆盖类对象后改变其值。因此我们需要撰写新的class来满足我们的需求
简单点来说,就是1、不能直接用int作为value的类型,因为HashMap的
我的思路
我也尝试了一下,如果直接写成
hm.get(k)++;
是不行的,会报invalid argument to operation ++/–,即该类型不支持直接的++/–操作。
有没有办法解决呢?其实是有的,我的方法利用了HashMap不允许重复key值的特点,换句话说就是,如果HashMap收到了重复的key值,会用新的value将原来的value覆盖
代码实现如下:
public static void main(String[] args) {
HashMap<Integer,Integer> hm = new HashMap<>();
for(int i = 0;i < 10000;i++){
Integer k = (int)(Math.random()*20);
if(hm.containsKey(k)){
hm.put(k, (hm.get(k)+1));
}else{
hm.put(k, new Integer(1));
}
}
System.out.println(hm);
}
这里使用了覆盖的方法,不必新建类也能实现key值的替换。代码运行结果如下图:
{0=527, 1=520, 2=495, 3=475, 4=476, 5=499, 6=501, 7=510, 8=489, 9=544, 10=520, 11=511, 12=481, 13=481, 14=501, 15=490, 16=489, 17=472, 18=507, 19=512}
与原著中期望结果一致(随机数次数每次均不同,会有差异)
总结
我的这种方法比较讨巧,写起来会容易一些,是一种面向过程的编程思路。但如果数据过大,我觉得在效率上不如原著。
于是我做了进一步的测试
将随机数改为10000000(1千万)后,测试100次运行平均时间(ms),结果如下:
原著方法 平均值:
404
我的方法 平均值:
561
我觉得原因如下:
我的方法由于每次需要提取value,做出修改后又填入,效率上会低不少。
而原著的方法则是直接对对象的变量进行++运算操作,更加容易理解,实现起来难度较小。
综上,其实我更认同原著的方法,在面向对象的编程领域,就应该用面向对象的思想来解决问题。我的方法是为了实现而去实现,只能说提供了一种解决该问题的新的思路和尝试。虽然问题很简单,但我仍想记下来,留给以后的自己看一看。