+警告

这篇文章作废掉,是由一个错误的测试方法得到的一个错误结论,后续修正结果正在测试,将贴上。

虽然java8出来很久了,但是之前用的一直也不多,最近正好学习了java8,推荐一本书还是不错的。因为学习了Java8,所以只要能用到的地方都会去用,尤其是Java8的Stream,感觉用起来觉得很方便,因为点点点就出来了,而且代码那么简洁。现在开始慢慢深入了解java8,发现很多东西不能看表面。

比如常规遍历一个集合,下面给出例子:

1.首先遍历一个List

方式1.一开始是这样的:

public static void test1(Listlist) {for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}

方式2.当然稍微高级一点的是这样:

public static void test2(Listlist) {for (int i = 0,lengh=list.size(); i < lengh; i++) {
System.out.println(list.get(i));
}
}

方式3.还有就是Iterator遍历:

public static void test3(Listlist) {
Iterator iterator =list.iterator();while(iterator.hasNext()){
System.out.println(iterator.next());
}
}

方式4.后来有了增强for循环:

public static void test4(Listlist) {for(String str:list){
System.out.println(str);
}
}

方式5.java8以后新增的方式:

public static void test5(Listlist) {//list.forEach(System.out::println);和下面的写法等价
list.forEach(str->{
System.out.println(str);
});
}

方式6.还有另一种:

public static void test6(Listlist) {
list.iterator().forEachRemaining(str->{
System.out.println(str);
});
}

应该没有其他的了吧,上面六中方法,按我的使用习惯5最常用,4偶尔使用,其他的基本就不怎么用了,使用5的原因是因为方便书写,提示就可以写出来,偶尔使用4的原因是,5不方便计数用,下面进行性能测试,String不具备代表性,决定使用对象,简单的一个测试类如下:

一个简单的测试,内容不要太在意,简单计算hashCode:

packagetest;importjava.util.ArrayList;importjava.util.Iterator;importjava.util.List;public classTest8 {public static voidmain(String[] args) {
List list=new ArrayList<>();for(int i=0;i<10;i++){
list.add(new Dog(i,"dog"+i));
}long nanoTime =System.nanoTime();
test1(list);long nanoTime1 =System.nanoTime();
test2(list);long nanoTime2 =System.nanoTime();
test3(list);long nanoTime3 =System.nanoTime();
test4(list);long nanoTime4 =System.nanoTime();
test5(list);long nanoTime5 =System.nanoTime();
test6(list);long nanoTime6 =System.nanoTime();
System.out.println((nanoTime1-nanoTime)/1000000.0);
System.out.println((nanoTime2-nanoTime1)/1000000.0);
System.out.println((nanoTime3-nanoTime2)/1000000.0);
System.out.println((nanoTime4-nanoTime3)/1000000.0);
System.out.println((nanoTime5-nanoTime4)/1000000.0);
System.out.println((nanoTime6-nanoTime5)/1000000.0);
}public static void test1(Listlist) {for (int i = 0; i < list.size(); i++) {
list.get(i).hashCode();
}
}public static void test2(Listlist) {for (int i = 0,lengh=list.size(); i < lengh; i++) {
list.get(i).hashCode();
}
}public static void test3(Listlist) {
Iterator iterator =list.iterator();while(iterator.hasNext()){
iterator.next().hashCode();
}
}public static void test4(Listlist) {for(Dog dog:list){
dog.hashCode();
}
}public static void test5(Listlist) {//list.forEach(System.out::println);和下面的写法等价
list.forEach(dog->{
dog.hashCode();
});
}public static void test6(Listlist) {
list.iterator().forEachRemaining(dog->{
dog.hashCode();
});
}
}classDog{private intage;privateString name;public Dog(intage, String name) {super();this.age =age; =name;
}public intgetAge() {returnage;
}public void setAge(intage) {this.age =age;
}publicString getName() {returnname;
}public voidsetName(String name) { =name;
}
@OverridepublicString toString() {return "Dog [age=" + age + ", name=" + name + "]";
}
}


运行三次取平均值,机器配置就不说了,因为我不是比较的绝对值,我是比较的这几种方式的相对值,数据结果,趋势图如下:

java stream foreach怎么并行 java list.stream.foreach_java

然后去掉表现一直很稳定的方式5和百万级数据量以上的数据,来分析结果:

java stream foreach怎么并行 java list.stream.foreach_List_02

可以得出一个非常吓人的结果,java8的foreach每次循环的耗时竟然高达100毫秒以上,虽然它比较稳定(算是优点吧)。所以得出以下结论:

在正常使用(数据量少于百万以下),正常(非并行)遍历一个集合的时候:

不要使用java8的foreach,每次耗时高达100毫秒以上

提前计算出大小的普通for循环,耗时最小,但是书写麻烦

增强for循环表现良好

测试LinkedList

只为了验证最后的观点,所以只测试增强for循环和java8foreach循环:

java stream foreach怎么并行 java list.stream.foreach_java8循环_03

经过一轮测试,java8foreach一如既往的稳定高耗时,增强for循环依然很给力。

2.再次遍历一个Set

使用以相同的方式测试HashSet,测试方法如下:

packagetest;importjava.util.HashSet;importjava.util.Iterator;importjava.util.Set;public classTest9 {public static voidmain(String[] args) {
Set set = new HashSet<>();for (int i = 0; i < 10_000_000; i++) {
set.add(new Dog(i, "dog" +i));
}long nanoTime =System.nanoTime();
test1(set);long nanoTime1 =System.nanoTime();
test2(set);long nanoTime2 =System.nanoTime();
test3(set);long nanoTime3 =System.nanoTime();
test4(set);long nanoTime4 =System.nanoTime();
System.out.println((nanoTime1- nanoTime) / 1000000.0);
System.out.println((nanoTime2- nanoTime1) / 1000000.0);
System.out.println((nanoTime3- nanoTime2) / 1000000.0);
System.out.println((nanoTime4- nanoTime3) / 1000000.0);
}public static void test1(Setlist) {
Iterator iterator =list.iterator();while(iterator.hasNext()) {
iterator.next().hashCode();
}
}public static void test2(Setlist) {for(Dog dog : list) {
dog.hashCode();
}
}public static void test3(Setlist) {
list.forEach(dog->{
dog.hashCode();
});
}public static void test4(Setlist) {
list.iterator().forEachRemaining(dog->{
dog.hashCode();
});
}
}


经过计算得出如下结果:

java stream foreach怎么并行 java list.stream.foreach_java8循环_04

不难发现,java8的foreach依然每次耗时100ms以上,最快的变成了增强for循环,Iterator遍历和java8的iterator().forEachRemaining差不多。

3.最后遍历Map

依然使用相同的方式测试Map集合遍历,测试类如下:

packagetest;importjava.util.HashMap;importjava.util.Iterator;importjava.util.Map;importjava.util.Set;public classTest10 {public static voidmain(String[] args) {
Map map = new HashMap<>();for (int i = 0; i < 1000_000; i++) {
map.put("dog" + i, new Dog(i, "dog" +i));
}long nanoTime =System.nanoTime();
test1(map);long nanoTime1 =System.nanoTime();
test2(map);long nanoTime2 =System.nanoTime();
test3(map);long nanoTime3 =System.nanoTime();
test4(map);long nanoTime4 =System.nanoTime();
System.out.println((nanoTime1- nanoTime) / 1000000.0);
System.out.println((nanoTime2- nanoTime1) / 1000000.0);
System.out.println((nanoTime3- nanoTime2) / 1000000.0);
System.out.println((nanoTime4- nanoTime3) / 1000000.0);
}public static void test1(Mapmap) {
Iterator> entries =map.entrySet().iterator();while(entries.hasNext()) {
Map.Entry entry =entries.next();int code=entry.getKey().hashCode()+entry.getValue().hashCode();
}
}public static void test2(Mapmap) {for (Map.Entryentry : map.entrySet()) {int code=entry.getKey().hashCode()+entry.getValue().hashCode();
}
}public static void test3(Mapmap) {for(String key : map.keySet()) {int code=key.hashCode()+map.get(key).hashCode();
}
}public static void test4(Mapmap) {
map.forEach((key, value)->{int code=key.hashCode()+value.hashCode();
});
}
}


结果如下:

java stream foreach怎么并行 java list.stream.foreach_List_05

java8的foreach依然不负众望,最快的是增强for循环。

+最终结论

普通(数量级10W以下,非并行)遍历一个集合(List、Set、Map)如果在意效率,不要使用java8的foreach,虽然它很方便很优雅

任何时候使用增强for循环是你不二的选择