这里提到的集合包括数组,那么在使用Java的过程中,关于集合,相信我们最常用的莫过于List、Set和数组了。在JDK的API文档中,在网上的无数博客中,都会找到List转Set、Set转List、Array转List等办法,这里也不再重复阐述了。
写这篇博客的目的,一是记录自己在写代码过程中的思考过程;二是将自己思考后的结论分享出来,希望对再遇见类似情况的朋友有所启示。
本人在几天前的写代码过程中,需要将一个String数组转为Set。使用List习惯了,Array转List,Arrays.asList(arr)对吧。所以本人本能反应去写Arrays.asSet时发现没有!那么肯定还有别的方法实现String数组转Set的需求。
我们可以使用JDK API的方法很方便地实现转换:
Set strSet = new HashSet(Arrays.asList(arr));
还有别的办法吗?当然有!最原始的,循环赋值转换:
Set strSet = new HashSet();
for (String item : arr) {
strSet.add(item);
}
本人选用了第二种方法,原因是直观感觉第一种方式中需要将数组先转为List,在将这个List转为Set,跟第二种方式相比除了都需要循环赋值,还多了转List这一步。所以本人主观认为第二种方式虽然原始,但效率应该不会差~虽然数据量小的根本就谈不上效率不效率的。
但是当review代码时,同事提出了质疑。一是觉着我这种写法太笨拙,二是通过网上搜索,stackoverflow中赞同最多的啊,Array转Set,竟然是第一种方式!所以他质疑第一种方式未必比第二种方式效率差。OK,实践出真知嘛,下面是本人写的测试代码:
public static void main(String[] args) throws ParseException {
String[] strArr = new String[1000000];
for (int i = 0; i < strArr.length; i++) {
strArr[i] = String.valueOf(i);
}
Set ids = new HashSet();
long start = System.currentTimeMillis();
for (String str : strArr) {
ids.add(str);
}
long end = System.currentTimeMillis();
System.out.println("================== array to set time: " + (end - start));
start = System.currentTimeMillis();
ids = new HashSet(Arrays.asList(strArr));
end = System.currentTimeMillis();
System.out.println("================== array to list to set time: " + (end - start));
}
下面是运行的结果: ================== array to set time: 648 ================== array to list to set time: 331
随着数组size的递增,运行结果的差值越明显。那么得出的结论,并不是本人想当然所认为的array to list to set会更费时些。查阅了JDK源码发现了有一个关键点,使得使用JDK API将array转set更高效。HashSet的构造函数如下所示。
public HashSet(Collection extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
其中最关键的就是new HashMap那行所赋值的初始容量。学习集合时我们应该了解,集合在扩容时是比较消耗资源的,所以如果能控制好要使用的集合的容量,减少Java集合自动的扩容操作,自然会提升集合的使用效率~