专栏首页ImportSourceJava8真不用再搞循环了?

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),作者:贺卓凡

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JVM的String Pool到底是个什么鬼?

    摘要:有关string,你一定看了不少的内容。你可能以前也看到过类似“new String(“xxx”)和String s2 = “Cat" 有什么区别?”之类...

    ImportSource
  • SpringCloud神兽之一:Zuul

    Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。Zuu...

    ImportSource
  • 设计模式-抽象类,只是想为你做更多

    如果说面向对象中的接口是把所有的事情扔给你的话,那么抽象类显然是想要为你做一些事情,如果实在有一部分做不了再扔给你。 相信对于大部分业务开发的场景下都不太会需要...

    ImportSource
  • 【SpringBoot WEB 系列】RestTemplate 之非 200 状态码信息捕获

    前面介绍的 RestTemplate 的使用,都是接口正常返回 200 的状态码 case,当返回非 200 状态码时,会直接抛异常,如果我希望能捕获异常,并针...

    一灰灰blog
  • 一文教你实现 SpringBoot 中的自定义 Validator 和错误信息国际化配置

    本文通过示例说明,在 Springboot 中如何自定义 Validator,以及如何实现国际化的错误信息返回。注意,本文代码千万别直接照抄,有可能会出大事情的...

    程序猿石头
  • Spring cloud zuul为什么需要FormBodyWrapperFilter

    Spring cloud zuul里面有一些核心过滤器,以前文章大致介绍了下各个过滤器的作用,武林外传—武三通的zuul之惑。这次重点讲解下FormBodyWr...

    java达人
  • Leetcode#344. Reverse String(反转字符串)

    武培轩
  • 聊聊rocketmq的BrokerHousekeepingService

    本文主要研究一下rocketmq的BrokerHousekeepingService

    codecraft
  • 第三十六章:基于SpringBoot架构重写SpringMVC请求参数装载

    恒宇少年
  • springboot 注解实现日志切面

    项目中很多时候需要去打印方法入参和出参的日志,有助于排查错误。 注解需要操作简单。 常用的方式之一就是使用切面来切日志。

    潇洒

扫码关注云+社区

领取腾讯云代金券