Java 8 Stream 深入

183 阅读4分钟

书接上文

热身运动

找出该流中大于2的元素,然后将每个元素乘以2,然后忽略掉流中的前两个元素,然后再取流中的前两个元素,最后求出流中元素的总和

  • 代码
public class StreamTest8 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1,2,3,5,7,8);
        stream
                .filter(item -> item > 2)// 过滤不大于2的元素 , 3,5,7,8
                .mapToInt(item -> item * 2)//每个元素乘2 ,     6,10,14,16
                .skip(2)//忽略前两个元素   14,16
                .limit(2)  //取前两个元素      14,16
                .reduce(Integer::sum) // 求和   14+16 = 30  ,返回optional
                .ifPresent(System.out::println);  //如果Optional不空就输出
    }
}

将流中字符串的首字母大写

public class StreamTest7 {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("aa","bb","cc","dd ee");
        stream.map(item -> item.substring(0,1).toUpperCase(Locale.ROOT)+item.substring(1))
        .forEach(System.out::println);
    }
}

image.png

求最大最小值,总和以及平均值(IntSummaryStatistics的使用)

public class StreamTest7 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(2,4,6,8,10,15);
                                                      //需要将Integer拆箱成int
        IntSummaryStatistics summaryStatistics =stream.mapToInt(Integer::valueOf).summaryStatistics();
        System.out.println(summaryStatistics);
    }
}

image.png

关于Stream的注意点

  • 何谓中间操作和终止操作?(点开Stream类的源码看看方法文档就可以知道是什么操作)

中间操作 image.png 终止操作

image.png

1、流不会改变底层的数据

image.png

2、没有终止操作stream的中间操作是不会执行的

  • 惰性求值: 没有终止操作,中间操作就不会执行
  • 及早求值: 一旦出现终止操作,立马执行中间操作

image.png

3、中间操作的返回值一般是一个新的Stream对象

  • 不被推荐但是正确的写法(本例只用来说明中间操作会返回新的Stream)
public class StreamTest7 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(2,4,6,8,10,15);
        Stream<Integer> stream1 = stream.map(item ->item*2);
        Stream<Integer> stream2 = stream1.filter(item -> item%2==0);
        Stream<Integer> stream3 = stream2.sorted((a,b)-> b-a);
        //Optional optional = stream3.max((a,b)->b-a);
        stream3.forEach(item -> System.out.print(item+" "));
        System.out.println("\nstream:"+stream+", stream1:"+stream1+", stream2:"+stream2);
        //应该用链式写法
    }
}

image.png

4、一个流一旦挂载了中间操作或终止操作就不能再继续使用

image.png

image.png

加深一下对Stream的理解

以sql语句来体验一下Stream

  • 以查询为例
select name from student where age > 20 and address = "上海" order by age desc
# 查询地址在上海年龄大于20 的学生名字 将其按年龄升序排
  • 在不用Stream时,用Java实现上面sql一样的功能
 List<Student> list = new ArrayList<>();
 for(Student stu : students){//这里还省略了students这么来的呢,是不是很麻烦
    if(stu.getAge() > 20 && stu.getAddress().equals("上海")){
         list.add(stu);
    }
 }
 Collections.sort(list,Comparetor()...);
 for(Student stu : list ){
    System.out.println(stu.getName());
 }
  • 使用Stream
students
        .stream()
        .filter(stu -> stu.getAge() > 20 && stu.getAddress().equals("上海"))
        .sort(...)
        .forEach(stu -> System.out.print(stu.gatName()+" ");

Stream一气呵成,几乎和sql形状类似

6、 流是短路操作

  • 何谓短路操作?

以Java谓词为例:

  • A && B 当A为假时B不会执行
  • C || D 当C为真时D不会执行
//找出第一个String长度为2,如果存在就输出 2
public class StreamTest9 {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("aa","bb","bbbdd");
        stream.map(item ->{//将String元素变成对应长度 int 
            int len = item.length();
            System.out.println(item+"  in map");
            return len;
        }).filter(item -> item==2).findFirst().ifPresent(System.out::println);
    }
}

map并不是遍历,只要找到符合条件的,流就结束了 image.png

进阶使用

并行流

和串行流的使用方法一样


public class ParallelStream {
    public static void main(String[] args) {
        Stream<String> stream =Stream.generate(UUID.randomUUID()::toString).limit(5000000);//提取固定个数的UUID
        List<String> list = stream.collect(Collectors.toList());//将5000000个UUID放入list
        long a = System.nanoTime();
        list.parallelStream().sorted().parallel().forEach(l ->System.out.print(""));
        long b = System.nanoTime();
        System.out.println("并行运行时间:"+TimeUnit.NANOSECONDS.toMillis(b-a));//排序和forEach的运行时间
        System.out.println("----------------");
        a = System.nanoTime();
        list.stream().sorted().forEach(l ->System.out.print(""));
        b = System.nanoTime();
        System.out.println("串行运行时间:"+TimeUnit.NANOSECONDS.toMillis(b-a));
    }
}

image.png

有时候并行不一定比串行快

flatMap的使用

  • 提取单词并去重
public class FlatMap {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("an apple","Make me crazy, but it’s fine","Make fine");
        stream
                .map(item -> item.split(" "))//返回Stream<String[]>
                .flatMap(Arrays::stream)//多个Stream.of(String[]),汇聚成一个Stream
                .distinct()//去重
                .collect(Collectors.toList())//变成List
                .forEach(System.out::println);//输出
    }
}

image.png

所以map返回Stream<String[]>

image.png

===================================

  • 将list中的一个元素分别拼接到另外一个集合的每个元素上
public class FlatMap {
    public static void main(String[] args) {
         List<String> list1 = Arrays.asList("Hi~ ","Hello~ ","你好~ ");
         List<String> list2 = Arrays.asList("王老板 ","赵老板 ","李老板 ");
         list1.stream()                                      //拼接
                 .flatMap(item -> list2.stream().map(item2 -> item + item2 ))//流中流
                 .collect(Collectors.toList())
                 .forEach(System.out::println);
    }
}

image.png

分组(Collectors.groupingBy)

按名字分组

public class GroupBy {
    public static void main(String[] args) {
        Student student1 = new Student("老王",80,13);
        Student student2 = new Student("老李",60,20);
        Student student3 = new Student("老赵",55,28);
        Student student4 = new Student("老王",90,10);
        Stream<Student> stream =Stream.of(student1,student2,student3,student4);
        Map<String, List<Student>> map = stream.collect(Collectors.groupingBy(Student::getName));
        map.forEach((key, value) -> {
            System.out.println(key + ":" + value);
        });
    }
}

image.png

分区(Collectors.partitioningBy)

  • 分区是分组的特殊化,根据一个条件分成ture区和false区

将及格和不及格分开

public class GroupBy {
    public static void main(String[] args) {
        Student student1 = new Student("老王",80,13);
        Student student2 = new Student("老李",60,20);
        Student student3 = new Student("老赵",55,28);
        Student student4 = new Student("老王",54,10);
        Stream<Student> stream =Stream.of(student1,student2,student3,student4);
        Map<Boolean, List<Student>> map = stream.collect(Collectors.partitioningBy(stu -> stu.getScore()>=60));
        map.forEach((key, value) -> {
            System.out.println(key + ":" + value);
        });
    }
}

image.png