Java8新特性之stream流操作使用

一、Java8新特性

  • Lambda表达式:允许把函数作为一个方法的参数传递到方法中。
  • 方法引用:可以直接引用已有Java类或对象(实例)的方法或构造器。
  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
  • 新工具 :Nashorn引擎 jjs、类依赖分析器jdeps。
  • Stream API :函数式编程风格引入。
  • Date Time API:加强对日期与时间的处理。
  • Optional 类 :用来解决空指针异常。
  • Nashorn, JavaScript 引擎 :允许我们在JVM上运行特定的javascript应用。

二、Stream API 介绍(特性之一)

  • 将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
  • 不会存储元素,按需计算。
  • 数据源流的来源可以是集合,数组,I/O channel, 产生器generator 等。
  • 类似SQL语句一样的聚合操作, 比如filter, map, reduce, find, match, sorted等。
  • 内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代,而Stream提供了内部迭代的方式。

三、具体使用

stream的操作符大体上分为两种:中间操作符和终止操作符

1. 生成流

  1. stream() − 为集合创建串行流。
  2. parallelStream() − 为集合创建并行流。

2. 中间操作符

  1. filter(T-> boolean):过滤数据,保留 boolean 为 true 的元素。

    //假设:list(20, 23, 25, 28, 30, 33, 37, 40);
    //从指定数据集合中过滤出大于等于30的数据集合
    //collect(Collectors.toList())可以把流转换为 List 类型,
    //collect实际上是一个终止操作。
    List<Integer> filterList= list.stream().filter(x -> x >= fil30).collect(Collectors.toList());
    //结果:[30, 33, 37, 40]
    System.out.println(filterList);
    
  2. map(T -> R):转换操作符,可以做数据转换。

    //假设:list("1", "2", "3", "4", "5", "6");
        List<Long> collect1 = list.stream()
        .map(x -> Long.parseLong(x))
        .collect(Collectors.toList());
        //结果:[1, 2, 3, 4, 5, 6]
    
        //结果:111111
        list.stream()
        .mapToInt(x -> x.length())
        .forEach(System.out::print);
    
        //结果:1.01.01.01.01.01.0
        list.stream()
        .mapToDouble(x -> x.length())
        .forEach(System.out::print);
    
  3. flatMap(T -> Stream):将元素T映射成流,再把每一个流连接成为一个整体流。

    	List<List<String>>  list = new ArrayList<List<String>>(){{
            add(Lists.newArrayList("a","b","c"));
            add(Lists.newArrayList("d","e","f"));
            add(Lists.newArrayList("j","k","y"));
        }};
        //结果:[[a, b, c], [d, e, f], [j, k, y]]
        
        List<String> collect = list.stream().flatMap(List::stream).collect(Collectors.toList());
        //结果:[a, b, c, d, e, f, j, k, y]
    
  4. distinct:去重,也可使用Collectors.toSet()达到去重目的。

    List<Integer> strings =new ArrayList<>(Arrays.asList(1,2,2,8,4,7)) ;
    //去除重复的4
    List<Integer> collect = strings.stream().map(i -> i * i).distinct().collect(Collectors.toList());
    //结果:[1, 4, 64, 16, 49]
    
  5. sorted:排序,前提是实现Comparable接口,也可自定义比较器。

    //假设:list(5, 3, 7, 1, 4, 6);
    List<Integer> collect = list.stream().sorted((a, b) -> a.compareTo(b)).collect(Collectors.toList());
    //结果:[1, 3, 4, 5, 6, 7]
    
  6. limit:限流操作,比如有5个元素,只取前3个元素。

    List<Integer> list = new ArrayList<>(Arrays.asList(1,2,2,8,4,7));
    List<Integer> collect = 
    list.stream()
    .limit(3)
    .collect(Collectors.toList());
    //结果:[1,2,2]
    
  7. skip:跳过操作,比如:有个10个元素,跳过5个元素,从第6个元素开始遍历。

    List<Integer> strings = new ArrayList<>(Arrays.asList(1,2,2,8,4,7));
        List<Integer> collect = list.stream().skip(2).collect(Collectors.toList());
        //结果:[2,8,4,7]
    
  8. peek:挑出操作,返回的stream仍旧是原来的stream。

    List<String> strings =  new ArrayList<>(Arrays.asList("a","b"));
    strings.stream().peek(x -> x.toUpperCase()).forEach(System.out::print);
    //结果:["a","b"]
    strings.stream().map(x -> x.toUpperCase()).forEach(System.out::print);
    //结果:["A","B"]
    

3. 终止操作符

  1. forEach:支持并行处理。forEachOrdered:按顺序处理的,遍历速度较慢。

    List<String> strings = new ArrayList<>(Arrays.asList("a","b"));
    list.stream().forEach(x-> System.out.print(x+' '));
    //结果:["a","b"]
    
    //简化
    list.forEach(x-> System.out.print(x+' '));
    
    list.stream().forEach(x-> System.out.print(x+' '));
    //结果:["a","b"]
    
  2. collect:收集操作,collector提供了很多的收集器。如:toMap、toSet、toList、joining等操作。

    //setter、getter、构造函数省略
    class User {
    private String name;
    private Integer age;
    }
    //假设user1={"jj",123},user2={"fs",1},user3={"te",1}
    //toSet去重,结果:[1, 123]
    Set<Integer> collect = users
    .stream()
    .map(User::getAge)
    .collect(Collectors.toSet());
    
    //toList,结果:[User{name='fs', age=1}, User{name='te', age=1}]
    //orElse(null):如果一个都没有,返回null
    List<User> collect = users
    .stream()
    .filter(user -> user.getAge() == 1)
    .collect(Collectors.toList())
    .orElse(null);
    
    //toMap,结果:{jj=User{name='jj', age=123}, te=User{name='te', age=1}, fs=User{name='fs', age=1}}
    Map<String, User> map  = users
    .stream()
    .collect(Collectors.toMap(User::getName, Function.identity()));
    
    //joining,假设list=["a","b"],结果:a,b,c
    String collect3 = list.stream().collect(Collectors.joining(","));
    
    //counting,结果:3(类似list.size())
    Long collect = users.stream().collect(Collectors.counting());
    
  3. find:有如下两个查找方法。
    findFirst:找到第一个
    findAny:使用 stream() 时找到的是第一个元素,使用 parallelStream() 并行时找到的是其中一个元素。

    //结果:User{name='jj', age=123}
    User user = users.stream().findFirst().orElse(null);
    
    //结果:User{name='jj', age=123}
    User user = users.stream().findAny().orElse(null);
    
  4. match:有如下三个匹配方法。
    allMatch:所有元素都满足条件,返回boolean类型
    anyMatch:任意一个元素满足条件,返回boolean类型
    noneMatch:所有元素都不满足条件,返回boolean类型

    //所有对象的年龄都大于0? 结果:true
    boolean b = users.stream().allMatch(user -> user.getAge() > 0);
    
    //存在对象的年龄小于0? 结果:false
    boolean b1 = users.stream().anyMatch(user -> user.getAge() < 0);
    
    //不存在对象的年龄大于200?  结果:true
    boolean b2 = users.stream().noneMatch(user -> user.getAge() > 200);
    
  5. count:统计操作,与集合的size()方法类似。

    //结果:3
    long count = users.stream().count();
    

4. min、max:获取最大,最小值。

//结果:User{name='jj', age=123}
Optional<User> max = users
.stream()
.max((User a, User b) -> a.getAge().compareTo(b.getAge()));
System.out.println(max.get());

5. reduce:规约操作,将整个流的值规约为一个值。

//结果:24
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,2,8,4,7));
Optional<Integer> reduce = list.stream().reduce((a, b) -> a + b);
System.out.println(reduce.get());

6. toArray:数组操作,将数据流的元素转换成数组。

//结果:[1,2,2,8,4,7]
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,2,8,4,7));
Integer[] integers = list.stream().toArray(Integer[]::new);

四、实际应用场景

1.从两个集合中找相同的元素。一般用户批量导入。先查询出数据,再比对,再进行新增或修改。

List<String> list1 = new ArrayList<>(Arrays.asList("ab","a","ad"));
List<String> list2 = new ArrayList<>(Arrays.asList("ac","a","ad"));
List<String> collect = list1.stream().filter(s -> list2.stream().anyMatch(s2 -> s2.equals(s))).collect(Collectors.toList());
        System.out.println(collect);

2.统计指定集合中,姓名相同的人中年龄最小的年龄。

//假设user1={"jj",123},user2={"jj",1},user3={"te",1}
User user = users
.stream()
.sorted((User u1, User u2) -> u1.getAge().compareTo(u2.getAge()))
.findFirst()
.orElse(null);
//结果:User{name='jj', age=1}
System.out.println(user);
上一篇:【方法引用、Lambda表达式、Stream流】


下一篇:Java如何将List转成Map,及其细节(Java中用Stream)