Java8真不用再搞循环了?

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(importsource)

原文发表时间:2017-04-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏pangguoming

Java编程最差实践(常见编程错误典范)

转载自  http://macrochen.iteye.com/blog/1393502

872
来自专栏程序猿DD

JDK 1.5 - 1.8 各版本的新特性总结

此文章意在借鉴前人经验,留作日后查看。如有侵犯,实属无意。我以后会注意,谢谢博友的提醒。也愿各大博友们能够共同学习和努力。

7216
来自专栏阿杜的世界

【译】Java 8的新特性—终极版1. 简介2. Java语言的新特性3. Java编译器的新特性4. Java官方库的新特性5. 新的Java工具6. JVM的新特性7. 结论8. 参考资料

前言: Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级。在Java Code Geeks上已经有很多介绍Java 8新特性的文章,...

1164
来自专栏决胜机器学习

PHP数据结构(三)——运用栈实现括号匹配

PHP数据结构(三)——运用栈实现括号匹配 (原创内容,转载请注明来源,谢谢) 栈在数据结构上是一种特殊的线性表,其限制是仅允许在表的一端进行插入和删除运算,...

4286
来自专栏光变

3.1 ASM-方法-结构

ASM-方法-结构 本章将会介绍如果使用ASM core API生成或者转换Java编译后的method。 本将开始会展示编译后的method,然后使用很多说...

1552
来自专栏小樱的经验随笔

【Java学习笔记之二十八】深入了解Java8新特性

前言: Java 8 已经发布很久了,很多报道表明java 8 是一次重大的版本升级。在Java Code Geeks上已经有很多介绍Java 8新特性的文章,...

3687
来自专栏我是攻城师

Apache Pig学习笔记之内置函数(三)

4414
来自专栏Android知识点总结

00--图解数据结构之开篇+集合基类

1348
来自专栏技术之路

【转】Go Interface 源码剖析

源网址:http://legendtkl.com/2017/07/01/golang-interface-implement/

1702
来自专栏维C果糖

Guava 指南 之「前置条件」

前置条件 Guava 提供了很多用于进行前置条件检查的工具,我们强烈建议静态导入这些方法。 每个方法都用三种形式: 没有额外的参数。抛出的任何异常都没有错误信息...

2087

扫码关注云+社区

领取腾讯云代金券