Java8以后真的不用循环了?真的不用了?
好吧,本文分享的内容是java8之前和java8之后一些代码的不同写法,我们会先介绍java8之前和java8之后不同的写法,然后我们会对二者进行性能测试,得出性能测试对比报告。最后会做一个简单的总结。涉及到java8的内容主要是stream。
好,现在开始。
前后写法对比
List<String> 变为大写
有一个问题:现在有5个字母的数组。要求把这五个字母全部变为大写。
你会怎么写?在java8之前,你也许会这样写:
List<String> alpha = Arrays.asList("a", "b", "c", "d");
//Before Java8
List<String> alphaUpper = new ArrayList<>();
for (String s : alpha) {
alphaUpper.add(s.toUpperCase());
}
System.out.println(alpha); //[a, b, c, d]
System.out.println(alphaUpper); //[A, B, C, D]
就是通过for来遍历,然后toUpperCase。
那么在java8下呢?你可以不用这么麻烦了:
// Java 8
List<String> collect = alpha.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println(collect); //[A, B, C, D]
而且stream还可以支持任何类型:
// Extra, streams apply to any data type.
List<Integer> num = Arrays.asList(1,2,3,4,5);
List<Integer> collect1 = num.stream().map(n -> n * 2).collect(Collectors.toList());
System.out.println(collect1); //[2, 4, 6, 8, 10]
是不是很方便。
List<T> 转换为 List<String>
接下来我们再看一种情况。就是当我们现在有一个list。然后list里是一个自定义的引用类型。然后我们需要遍历这个引用类型对象中的某个属性。
在java8之前怎么做呢?
class Staff {
private String name;
private int age;
private BigDecimal salary;
public Staff(String name,int age,BigDecimal salary){
this.name=name;
this.age=age;
this.salary=salary;
}
public String getName() {
return name;
}
public Staff setName(String name) {
this.name = name;
return this;
}
public int getAge() {
return age;
}
public Staff setAge(int age) {
this.age = age;
return this;
}
public BigDecimal getSalary() {
return salary;
}
public Staff setSalary(BigDecimal salary) {
this.salary = salary;
return this;
}
}
List<Staff> staff = Arrays.asList(
new Staff("importsource", 30, new BigDecimal(10000)),
new Staff("messi", 27, new BigDecimal(20000)),
new Staff("xavi", 33, new BigDecimal(30000))
);
//Before Java 8
List<String> result = new ArrayList<>();
for (Staff x : staff) {
result.add(x.getName());
}
System.out.println(result); //[importsource, messi, xavi]
在java8之后呢?
//Java 8
List<String> collect = staff.stream().map(x -> x.getName()).collect(Collectors.toList());
System.out.println(collect); //[importsource, messi, xavi]
List<object> 转换为 List<other object>
现在我们再假设一种情况。现在有一个list。然后我们需要这个list里的对象中的属性值一个个拿出来,然后封装到一个新的对象中,然后放入一个新的list。
这个时候java8怎么做的呢?
自然还是循环:
public class BeforeJava8 {
public static void main(String[] args) {
List<Staff> staff = Arrays.asList(
new Staff("importsource", 30, new BigDecimal(10000)),
new Staff("messi", 27, new BigDecimal(20000)),
new Staff("xavi", 33, new BigDecimal(30000))
);
List<StaffPublic> result = convertToStaffPublic(staff);
System.out.println(result);
}
private static List<StaffPublic> convertToStaffPublic(List<Staff> staff) {
List<StaffPublic> result = new ArrayList<>();
for (Staff temp : staff) {
StaffPublic obj = new StaffPublic();
obj.setName(temp.getName());
obj.setAge(temp.getAge());
if ("importsource".equals(temp.getName())) {
obj.setExtra("this field is for importsource only!");
}
result.add(obj);
}
return result;
}
}
class StaffPublic{
private String name;
private int age;
private String extra;
public StaffPublic(String name,int age,String extra){
this.name=name;
this.age=age;
this.extra=extra;
}
public StaffPublic() {
}
public String getName() {
return name;
}
public StaffPublic setName(String name) {
this.name = name;
return this;
}
public int getAge() {
return age;
}
public StaffPublic setAge(int age) {
this.age = age;
return this;
}
public String getExtra() {
return extra;
}
public StaffPublic setExtra(String extra) {
this.extra = extra;
return this;
}
}
JAVA8之后的做法:
public class NowJava8 {
public static void main(String[] args) {
List<Staff> staff = Arrays.asList(
new Staff("importsource", 30, new BigDecimal(10000)),
new Staff("messi", 27, new BigDecimal(20000)),
new Staff("xavi", 33, new BigDecimal(30000))
);
// convert inside the map() method directly.
List<StaffPublic> result = staff.stream().map(temp -> {
StaffPublic obj = new StaffPublic();
obj.setName(temp.getName());
obj.setAge(temp.getAge());
if ("importsource".equals(temp.getName())) {
obj.setExtra("this field is for importsource only!");
}
return obj;
}).collect(Collectors.toList());
System.out.println(result);
}
}
ok,上面我们展示了java前后的不同写法。很明显java8新增的stream的写法更加的风骚和简便优雅,至少看起来蛮流行。
那么二者的性能如何呢?现在我们分别对java8之前的循环做法和java8之后的stream进行一个性能测试,看看结果。
fori VS Stream 性能对比
测试用例
public class PerformanceTest {
public static void main(String[] args) {
List<com.importsource.java8.streams.Staff> staff = Arrays.asList(
new com.importsource.java8.streams.Staff("importsource", 30, new BigDecimal(10000)),
new com.importsource.java8.streams.Staff("messi", 27, new BigDecimal(20000)),
new com.importsource.java8.streams.Staff("xavi", 33, new BigDecimal(30000))
);
// beforeJava8(staff);
// nowJava8(staff);
OneTest oneTest = new OneTest("nowJava8", 10000);
Function function = new Function() {
public void function(Object args) {
//beforeJava8(staff);
nowJava8(staff);
}
};
After after = new After() {
public void after(Object args) {
}
};
oneTest.setFunction(function);
oneTest.setAfter(after);
oneTest.start();
}
private static void nowJava8(List<Staff> staff) {
//Java 8
List<String> collect = staff.stream().map(x -> x.getName()).collect(Collectors.toList());
System.out.println(collect); //[importsource, messi, xavi]
}
private static void beforeJava8(List<Staff> staff) {
//Before Java 8
List<String> result = new ArrayList<>();
for (Staff x : staff) {
result.add(x.getName());
}
System.out.println(result); //[importsource, messi, xavi]
}
}
测试报告
Samples: 10000,单线程:
for循环:
#Samples:10000
Average:0.011
C-Average:0
medain:0
10% Line:0
30% Line:0
50% Line:0
90% Line:0
95% Line:0
99% Line:0
Min:0
Max:6
Total:110
Con-Total:158
Tps:63291
Error(%):0.0%
Success(%):100.0%
Java8 stream:
#Samples:10000
Average:0.023
C-Average:0
medain:0
10% Line:0
30% Line:0
50% Line:0
90% Line:0
95% Line:0
99% Line:0
Min:0
Max:85
Total:230
Con-Total:258
Tps:38759
Error(%):0.0%
Success(%):100.0%
Samples:1000,单线程:
for循环:
#Samples:1000
Average:0.027
C-Average:0
medain:0
10% Line:0
30% Line:0
50% Line:0
90% Line:0
95% Line:0
99% Line:0
Min:0
Max:2
Total:27
Con-Total:39
Tps:25641
Error(%):0.0%
Success(%):100.0%
Java8 stream:
#Samples:1000
Average:0.111
C-Average:0
medain:0
10% Line:0
30% Line:0
50% Line:0
90% Line:0
95% Line:0
99% Line:0
Min:0
Max:80
Total:111
Con-Total:123
Tps:8130
Error(%):0.0%
Success(%):100.0%
Samples:100,单线程:
for循环:
#Samples:100
Average:0.05
C-Average:0
medain:0
10% Line:0
30% Line:0
50% Line:0
90% Line:0
95% Line:0
99% Line:0
Min:0
Max:1
Total:5
Con-Total:14
Tps:7142
Error(%):0.0%
Success(%):100.0%
Java8 stream:
#Samples:100
Average:0.93
C-Average:1
medain:0
10% Line:0
30% Line:0
50% Line:0
90% Line:0
95% Line:0
99% Line:0
Min:0
Max:87
Total:93
Con-Total:101
Tps:990
Error(%):0.0%
Success(%):100.0%
总结
手画的一张图,java8中的map reduce的叫法其实就是借鉴了处理大数据的那个MapReduce。java8中的stream和java I/O中的那个stream有一点略微的不同。
I/O的那个stream更像是一个通道。而java8中的stream指的是在数据流转的过程中还包含有动态处理,就像上图中的一样,从输入然后被map分开,然后分拣合并到reduce,然后输出一个你想要的结果。
java8的stream是一种新的编程模型,它为java处理流数据或者说是处理集合提供了更方便的方式,而不像java8之前那么的笨重。
但是,java8之后就真的没必要用循环了吗?当然不是。stream只适合处理那些顺序next执行的逻辑。如果涉及到游标,则还是要用fori的。
关于性能。之前有人说stream性能较差。通过我们上面的测试对比发现,stream确实要比for循环慢不少。
到底是用循环还是stream,还是要看具体的场合。循环性能好,stream处理集合更加的方便友好。二者看起来都有各自的优势。
也许在对性能要求敏感的场景下,循环可能是个不错的选择。除此之外,可以去尝试下更加方便友好的stream。
------------
代码放到git 上了,点击“阅读原文”获取。
本文分享自 ImportSource 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!