Java 8 Stream 闪亮登场!

    科技2022-07-29  105

    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<Map<String, String>> mapList = new ArrayList() {         {             Map<String, String> m = new HashMap();             m.put("a", "1");             Map<String, String> m2 = new HashMap();             m2.put("b", "2");             add(m);             add(m2);         }     };     mapList.stream().forEach(item-> System.out.println(item));     //操作Map     Map<String,Object> 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<Integer> 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<Person> ps = new ArrayList<Person>(){         {             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<Integer> 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<Person> 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<personSize;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<Person> 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<personSize;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方法

    流到流之间的转换类

    filter(过滤)

    map(映射转换)

    mapTo[Int|Long|Double] (到基本类型流的转换)

    flatMap(流展开合并)

    flatMapTo[Int|Long|Double]

    sorted(排序)

    distinct(不重复值)

    peek(执行某种操作,流不变,可用于调试)

    limit(限制到指定元素数量)

    skip(跳过若干元素)

    流到终值的转换类

    toArray(转为数组)

    reduce(推导结果)

    collect(聚合结果)

    min(最小值)

    max(最大值)

    count (元素个数)

    anyMatch (任一匹配)

    allMatch(所有都匹配)

    noneMatch(一个都不匹配)

    findFirst(选择首元素)

    findAny(任选一元素)

    直接遍历类

    forEach (不保证顺序遍历,比如并行流)

    forEachOrdered(顺序遍历)

    构造流类

    empty (构造空流)

    of (单个元素的流及多元素顺序流)

    iterate (无限长度的有序顺序流)

    generate (将数据提供器转换成无限非有序的顺序流)

    concat (流的连接)

    Builder (用于构造流的Builder对象)

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

    关注Java技术栈看更多干货

    戳原文,获取精选面试题!

    Processed: 0.013, SQL: 8