Java 8 Stream 闪亮登场!

Java技术栈

共 8884字,需浏览 18分钟

 ·

2020-10-06 03:32

Java技术栈

www.javastack.cn

关注阅读更多优质文章



作者:funnyZpC
出处:cnblogs.com/funnyzpc/p/10382053.html

一. Stream(流)是什么,干什么

Stream是一类用于替代对集合操作的工具类+Lambda式编程,他可以替代现有的遍历、过滤、求和、求最值、排序、转换等

二. Stream操作方式

  • 并行方式parallelStream

  • 顺序方式Stream

三. Stream优势

  • Lambda 可有效减少冗余代码,减少开发工作量

  • 内置对集合List、Map的多种操作方式,含基本数据类型处理

  • 并行Stream有效率优势(内置多线程)

四. Stream(流)的基本使用

  • 遍历forEach
@Test
public void stream() {
    //操作List
    List> mapList = new ArrayList() {
        {
            Map m = new HashMap();
            m.put("a""1");
            Map m2 = new HashMap();
            m2.put("b""2");
            add(m);
            add(m2);
        }
    };
    mapList.stream().forEach(item-> System.out.println(item));

    //操作Map
    Map mp = new HashMap(){
        {
            put("a","1");
            put("b","2");
            put("c","3");
            put("d","4");
        }
    };
    mp.keySet().stream().forEachOrdered(item-> System.out.println(mp.get(item)));
}
  • 过滤filter
List mapList = new ArrayList() {
{
    add(1);
    add(10);
    add(12);
    add(33);
    add(99);
}
};
//mapList.stream().forEach(item-> System.out.println(item));
mapList = mapList.stream().filter(item->{
return item>30;
}).collect(Collectors.toList());
System.out.println(mapList);
  • 转换map和极值
@Test
public void trans(){
    List ps = new ArrayList(){
        {
            Person p1 = new Person();
            p1.setAge(11);
            p1.setName("张强");

            Person p2 = new Person();
            p2.setAge(17);
            p2.setName("李思");

            Person p3 = new Person();
            p3.setAge(20);
            p3.setName("John");

            add(p1);
            add(p2);
            add(p3);
        }
    };
    
    //取出所有age字段为一个List
    List sumAge = ps.stream().map(Person::getAge).collect(Collectors.toList());
    System.out.println(sumAge);
    
    //取出age最大的那
    Integer maxAge =sumAge.stream().max(Integer::compare).get();
    System.out.println(maxAge);
}

class Person{

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

五. Stream(流)的效率+ 模拟非耗时简单业务逻辑

class Person{

    private String name;
    private int age;
    private Date joinDate;
    private String label;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getJoinDate() {
        return joinDate;
    }

    public void setJoinDate(Date joinDate) {
        this.joinDate = joinDate;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }
}
public class DataLoopTest {

    private static final Logger LOG= LoggerFactory.getLogger(DataLoopTest.class);

    private static final List persons = new ArrayList<>();
    static {
        for(int i=0;i<=1000000;i++){
            Person p = new Person();
            p.setAge(i);
            p.setName("zhangSan");
            p.setJoinDate(new Date());
            persons.add(p);
        }
    }

    /**
     * for 循环耗时 ===> 1.988
     * for 循环耗时 ===> 2.198
     * for 循环耗时 ===> 1.978
     *
     */
    @Test
    public void forTest(){
        Instant date_start = Instant.now();
        int personSize = persons.size();
        for(int i=0;i            persons.get(i).setLabel(persons.get(i).getName().concat("-"+persons.get(i).getAge()).concat("-"+persons.get(i).getJoinDate().getTime()));
        }
        Instant date_end = Instant.now();
        LOG.info("for 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  forEach 循环耗时 ===> 1.607
     *  forEach 循环耗时 ===> 2.242
     *  forEach 循环耗时 ===> 1.875
     */
    @Test
    public void forEach(){
        Instant date_start = Instant.now();
        for(Person p:persons){
            p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
        }
        Instant date_end = Instant.now();
        LOG.info("forEach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  streamForeach 循环耗时 ===> 1.972
     *  streamForeach 循环耗时 ===> 1.969
     *  streamForeach 循环耗时 ===> 2.125
     */
    @Test
    public void streamForeach(){
        Instant date_start = Instant.now();
        persons.stream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
        Instant date_end = Instant.now();
        LOG.info("streamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  parallelStreamForeach 循环耗时 ===> 1.897
     *  parallelStreamForeach 循环耗时 ===> 1.942
     *  parallelStreamForeach 循环耗时 ===> 1.642
     */
    @Test
    public void parallelStreamForeach(){
        Instant date_start = Instant.now();
        persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
        Instant date_end = Instant.now();
        LOG.info("parallelStreamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

}
  • 模拟耗时简单业务逻辑
public class DataLoopBlockTest {

    private static final Logger LOG= LoggerFactory.getLogger(DataLoopTest.class);

    private static final List persons = new ArrayList<>();
    static {
        for(int i=0;i<=100000;i++){
            Person p = new Person();
            p.setAge(i);
            p.setName("zhangSan");
            p.setJoinDate(new Date());
            persons.add(p);
        }
    }

    /**
     * for 循环耗时 ===> 101.385
     * for 循环耗时 ===> 102.161
     * for 循环耗时 ===> 101.472
     *
     */
    @Test
    public void forTest(){
        Instant date_start = Instant.now();
        int personSize = persons.size();
        for(int i=0;i            try {
                Thread.sleep(1);
                persons.get(i).setLabel(persons.get(i).getName().concat("-"+persons.get(i).getAge()).concat("-"+persons.get(i).getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        Instant date_end = Instant.now();
        LOG.info("for 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  forEach 循环耗时 ===> 101.027
     *  forEach 循环耗时 ===> 102.488
     *  forEach 循环耗时 ===> 101.608
     */
    @Test
    public void forEach(){
        Instant date_start = Instant.now();
        for(Person p:persons){
            try {
                Thread.sleep(1);
                p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        Instant date_end = Instant.now();
        LOG.info("forEach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  streamForeach 循环耗时 ===> 103.246
     *  streamForeach 循环耗时 ===> 101.128
     *  streamForeach 循环耗时 ===> 102.615
     */
    @Test
    public void streamForeach(){
        Instant date_start = Instant.now();
        //persons.stream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
        persons.stream().forEach(p->{
            try {
                Thread.sleep(1);
                p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        Instant date_end = Instant.now();
        LOG.info("streamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  parallelStreamForeach 循环耗时 ===> 51.391
     *  parallelStreamForeach 循环耗时 ===> 53.509
     *  parallelStreamForeach 循环耗时 ===> 50.831
     */
    @Test
    public void parallelStreamForeach(){
        Instant date_start = Instant.now();
        //persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
        persons.parallelStream().forEach(p->{
            try {
                Thread.sleep(1);
                p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        Instant date_end = Instant.now();
        LOG.info("parallelStreamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
        //LOG.info("\r\n===> {}",JSON.toJSONString(persons.get(10000)));
    }

}

可以看到在百万数据下做简单数据循环处理,对于普通for(for\foreach)循环或Stream(并行、非并行)下,几者的效率差异并不明显。

注意: 在百万数据下,普通for、foreach循环处理可能比Stream的方式快许多,对于这点效率的损耗,其实lambda表达式对代码的简化更大!

另外,在并行流的循环下速度提升了一倍之多,当单个循环耗时较多时,会拉大与前几者的循环效率  (以上测试仅对于循环而言,其他类型业务处理,比如排序、求和、最大值等未做测试,个人猜测与以上测试结果相似)

六. Stream(流)注意项

  • 并行Stream不是线程安全的,当对循坏外部统一对象进行读写时候会造成意想不到的错误,这需要留意
  • Stream总是惰性的,原对象是不可以被修改的,在集合处理完成后需要将处理结果放入一个新的集合容器内
  • 普通循环与Stream(非并行)循环,在处理处理数据量比较大的时候效率是一致的,推荐使用Stream的形式
  • 对于List删除操作,目前只提供了removeIf方法来实现,并不能使用并行方式
  • 对于lambda表达式的写法
  • 当表达式内只有一个返回boolean类型的语句时候语句是可以简写的,例如:
persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
  • 当表达式内会有一些复杂处理逻辑时需要加上大括号,这与初始化List参数方式大致一致
try {
        Thread.sleep(1);
        p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
    }catch (Exception e){
        e.printStackTrace();
    }
});

七. stream&Lambda表达式常用api方法


好了,本篇到这里了,如果你想阅读更多 Java 8 + 系列教程,可以关注公众号Java技术栈回复 java 获取,我都写了一大堆了。





关注Java技术栈看更多干货



戳原文,获取精选面试题!
浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报