文章目录
#想法
在JDK-8中,新添加了很多的方法,比如说现在我所说的forEach,该方法是用于集合遍历的,其方式相当于传统的for循环遍历方式,只是与其不同之处就在于采用了lambda表达式,因而在进行循环遍历时代码更加的简介。
但是我们知道,在JDK-8中,除了可以采用forEach来进行集合遍历之外,还可以采用流的形式来对集合进行遍历操作,比如说stream,因而我就突然想到了比较一下三者执行效率到底如何。
#设计
下面就是我对于该一想法的实现,由于以上三种方法中含有流,所以说我们操作的对象就选择为实体类,因而我创建了一个实体类Demo,在该实体类中一共存放了两个字段,分别为index索引值字段以及str字符串字段。其中index索引值字段存放的是我们要取用的信息,而str字段则是属于干扰字段,实际上并没有取用。接下来我就是创建100万个Demo,在其中存放相应的信息,然后再使用不同的方法对其进行遍历。最后一步就是比较for、forEach与stream中两两执行时间差异并由此得出他们的执行效率排序。
#实现
Demo中的源码:
package com.lyc;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = {"index"})
public class Demo {
private int index;
private String str;
}
TraverseComparisonTest中的源码:
package com.lyc;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
public class TraverseComparisonTest {
List<Demo> demoList = null;
List<Integer> traditionList = null;
List<Integer> forEachList = null;
List<Integer> streamList = null;
/** * 准备数据 */
@Before
public void prepareDatabase(){
demoList = Lists.newArrayList();
traditionList = Lists.newArrayList();
forEachList = Lists.newArrayList();
streamList = Lists.newArrayList();
String str = "这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;" +
"这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;" +
"这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;这个是干扰字段;" +
"这个是干扰字段;这个是干扰字段;这个是干扰字段;";
for(int i = 0;i < 1000000;i ++){
Demo demo = new Demo(i,str);
demoList.add(demo);
}
}
/** * 传统的方式 * @return */
public long traditionMethod(){
Date date = new Date();
int traditionListLength = traditionList.size();
for(int i = 0;i < traditionListLength;i ++){
traditionList.add(demoList.get(i).getIndex());
}
Date date2 = new Date();
return date2.getTime() - date.getTime();
}
/** * forEach方式 * @return */
public long forEachMethod(){
Date date = new Date();
demoList.forEach(demo -> {
forEachList.add(demo.getIndex());
});
Date date2 = new Date();
return date2.getTime() - date.getTime();
}
/** * stream方式 * @return */
public long streamMethod(){
Date date = new Date();
streamList = demoList.stream().map(demo -> demo.getIndex()).collect(Collectors.toList());
Date date2 = new Date();
return date2.getTime() - date.getTime();
}
/** * 比较stream与传统的取值方式效率 */
@Test
public void test(){
long time1 = traditionMethod();
long time2 = forEachMethod();
long time3 = streamMethod();
log.info("for与forEach比较结果为:" + (time1 - time2));
log.info("forEach与stream比较结果为:" + (time2 - time3));
log.info("for与stream比较结果为:" + (time1 - time3));
}
}
执行结果:
2018-03-03 15:20:14 INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:85) - for与forEach比较结果为:-78
2018-03-03 15:20:14 INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:86) - forEach与stream比较结果为:-678
2018-03-03 15:20:14 INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:87) - for与stream比较结果为:-756
#结论
通过上述比较,在共同对Demo遍历100万次的过程中,for循环比forEach快78毫秒,而forEach又比stream快678毫秒,而for比stream快756毫秒。参考代码量与执行效率,我们不难得出如下结论:
- 它们的执行效率排序为 :
for > forEach > stream
- 它们的代码编写量排序为:
for < forEach < stream
#建议
在编写代码的过程中,我们究竟采用哪种遍历方式比较好?!这个得有一个选择标准,如果单纯的以运行效率为主,那还是采用传统的for循环,若要简化代码量,那就选用stream。不过我给大家的建议是多使用stream而不是运行效率最高的for循环,理由如下:
回顾编程的发展历史,我们不难发现一个规律,那就是先是从最初的C/C++演变到Java/.net,这是编程界的一大进步,因为我们不再关注于指针操作,比如在java中JVM虚拟机已经帮我们完成了相应的操作,由于这一进步,这付出的代价是执行效率会降低,但是带来的好处就在于加快了编程开发的速度。
当编程由Java/.net演变到JavaScript/PHP/Kotlin,这又是编程界的另一大进步,这意味着我们在编写程序时没有必要再关注于数据类型,而该数据类型是由相应的语言在运行时确定,这样,这又一次降低了程序的运行速度,但是相应的又提升了代码编写的效率,因而通过回顾历史我们不难得出如下结论:
在编写代码时,一定要以最简洁为原则,毕竟运行程序的硬件成本会随着时间的推移在不断降低,而程序员的薪资则不会。