试着实现一个更好的计数器.能够对输入的List
进行计数.java
最终实现版本使用泛型,使得能够对任意对象进行技术,可是在编写过程当中,先以String为例.bash
那么计数
这个行为的输入值是List<String>
,输出值为Map<String,Integer>
. 这里不是强行要求Integer
的,只要可以标识数量便可.多线程
直接随手写一个:学习
HashMap<String,Integer> c = new HashMap<>();
stringList.forEach(per->{
c.put(per, c.getOrDefault(per, 0) + 1);//步骤1
});
return c;
}
复制代码
这里面有几个点:测试
先解决第一个问题,封装一个可变的Integer类或者使用AtomicInteger. 在没有多线程的要求下,本身封装一个:优化
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);
}
}
复制代码
对于每个字符串,都须要get确认,而后put新值,这明显是不科学的.this
HashMap
的put
方法,实际上是有返回值的,会返回旧值.url
这就意味着咱们能够经过一次map操做来达到目的.spa
通过这样两次的优化,如今的方法为:线程
public static Map<String, MutableInteger> count2(List<String> strings) {
HashMap<String, MutableInteger> c = new HashMap<>();
strings.forEach(per -> {
MutableInteger init = new MutableInteger(1);
MutableInteger last = c.put(per, init);
if (last != null) {
init.set(last.get() + 1);
}
});
return c;
}
复制代码
public static void main(String[] args) {
List<String> list = new ArrayList<>();
String[] ss = {"my", "aa", "cc", "aa", "cc", "b", "w", "sssssa", "10", "10"};
for (int i = 0; i < 100000000; i++) {
list.add(ss[i % 10]);
}
long s = System.currentTimeMillis();
System.out.println(count1(list));
System.out.println(System.currentTimeMillis() - s);
long s1 = System.currentTimeMillis();
System.out.println(count2(list));
System.out.println(System.currentTimeMillis() - s1);
}
复制代码
测试结果以下:
{aa=20000000, cc=20000000, b=10000000, w=10000000, sssssa=10000000, my=10000000, 10=20000000}
4234
{aa=20000000, cc=20000000, b=10000000, w=10000000, sssssa=10000000, my=10000000, 10=20000000}
951
复制代码
能够看到结果很是明显,效率提升了4倍.
NOTE: 这个测试明显是有偏向的,由于我这个1亿条数据,只有几种,因此数据重复率很是高.可是平常使用中数据重复率不会有这么夸张. 可是构建1亿条重复率不高的测试数据,太麻烦了.
其实起做用比较大的是可变的Integer类.
而map的操做咱们知道,取和放到时O(1)的.因此这个的提高不是特别的大.经测试,修改成两次操做,仅增长80ms.
实现了如下几个API:
List
的形式.Map<T,Integer>
package daily.counter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** * Created by pfliu on 2019/04/21. */
public class Counter<T extends Object> {
private HashMap<T, MutableInteger> c = new HashMap<>();
public void add(T t) {
MutableInteger init = new MutableInteger(1);
MutableInteger last = c.put(t, init);
if (last != null) {
init.set(last.get() + 1);
}
}
public void addAll(List<T> list) {
list.forEach(this::add);
}
public int get(T t) {
return c.get(t).val;
}
public Map<T, Integer> getAll() {
Map<T, Integer> ret = new HashMap<>();
c.forEach((key, value) -> ret.put(key, value.val));
return ret;
}
public static final class MutableInteger {
private int val;
MutableInteger(int val) {
this.val = val;
}
public int get() {
return this.val;
}
void set(int i) {
this.val = i;
}
}
}
复制代码
固然你彻底不用本身实现,网上一大把已经实现的.
可是本身思考一下为何要这样实现,仍是有不少的好处的.
以上皆为我的所思所得,若有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文连接。
联系邮箱:huyanshi2580@gmail.com
更多学习笔记见我的博客------>呼延十