Java8新特性之集合的流式编程

Java8 的新特性 – 集合的流式编程


1、集合流的简介


1.1、集合的流式编程的简介

​ Stream是JDK1.8之后出现的新特性,也是JDK1.8新特性中最值得学习的两种新特性之一。(另外一个是lambda表达式)。

​ Stream是对集合操作的增强,流不是集合的元素,不是一种数据结构,不负责数据的存储的。流更像是一个迭代器,可以单向的遍历一个集合中的每一个元素,并且不可循环。

注意事项:关于流的操作还可以使用数组,例:

Arrays.stream(list).(...)...
1.2、为什么要使用集合的流式编程

​ 有些时候,对集合中的元素进行操作的时候,需要使用到其他操作的结果。在这个过程中,集合的流式编程可以大幅度的简化代码的数量。将数据源中的数据,读取到一个流中,可以对这个流中的数据进行操作(删除、过滤、映射…)。每次的操作结果也是一个流对象,可以对这个流再进行其他的操作。

1.3、使用流式编程的步骤

​ 通常情况下,对集合中的数据使用流式编程,需要经过以下三步。

​ 1、获取数据源,将数据源中的数据读取到流中。

​ 2、对流中的数据进行各种各样的处理。

​ 3、对流中的数据进行整合处理。

​ 在上述三个过程中,过程2中,有若干方法,可以对流中的数据进行各种各样的操作,并且返回流对象本身,这样的操作,被称为 – 中间操作。过程3中,有若干方法,可以对流中的数据进行各种处理,并关闭流,这样的操作,被称为 – 最终操作。

​ 在中间操作和最终操作中,基本上所有的方法参数都是函数式接口,可以使用lambda表达式来实现。使用集合的流式编程,来简化代码量,是需要对 lambda 表达式做到熟练掌握。

2、数据源的获取


2.1、数据源的简介

​ 数据源,顾名思义,既是流中的数据的来源。是集合的流式编程的第一步,将数据源中的数据读取到流中,进行处理。注意:将数据读取到流中进行处理的时候,与数据源中的数据没有关系。也就是说,中间操作对流中的数据进行处理、过滤、映射、排序…,此时是不会影响数据源中的数据的。

2.2、数据源的获取

​ 这个过程,其实是将一个容器中的数据,读取到一个流中。因此无论什么容器作为数据源,读取到流中的方法返回值一定是一个Stream。

// 1、通过Collection接口中的stream()方法获取数据源为Collection的流
Stream<Integer> stream = list.stream();
// 2、通过Collection接口的parallelStream()方法获取数据源为Collection的流
Stream<Integer> stream = list.parallelStream();
// 3、通过Arrays工具类中的stream()方法获取数据源为数组的流
IntStream stream = Arrays.stream(array);

关于 stream()parallelStream

​ 他们都是Collection集合获取数据源的方法,不同点在于stream()方法获取的数据源是串行的,parallelStream()获取的数据源是并行的。parallelStream()内部集成了多个线程对流中的数据进行操作,效率更高。

3、最终操作


3.1、最终操作的简介

​ 将流中的数据整合到一起,可以存入一个集合,也可以直接对流中的数据进行遍历、数据统计…,通过最终操作,需要掌握如何从流中提取出来我们想要的信息。

​ 注意事项:最终操作,之所以叫最终操作,是因为,在最终操作执行结束后,会关闭这个流,流中的所有数据都会销毁。如果使用一个已经关闭了的流,会出现异常。

3.2、collect

​ 将流中的数据收集到一起,对这些数据进行一些处理。最常见的处理,就是将流中的数据存入一个集合。collect方法的参数,是一个Collector接口,而且这个接口并不是一个函数式接口。实现这个接口,可以自定义收集的规则。但是,绝大部分情况下,不需要自定义。

​ 直接使用 Collectors 工具类提供的方法即可。

// 1.1、转成 List
List<Integer> result1 = list.stream().collect(Collectors.toList());
System.out.println(result1);

// 1.2、转成 Set
Set<Integer> result2 = list.stream().collect(Collectors.toSet());
System.out.println(result2);

// 1.3、转成 Map,提供两个函数式接口的实现,分别实现键的生成规则和值的生成规则
Map<Integer, Integer> result3 = list.stream().collect(Collectors.toMap(ele -> ele / 10, ele -> ele));
System.out.println(result3);
3.3、reduce

​ 将流中的数据按照一定的规则聚合起来。

// 将流的元素,逐一带入到这个方法中,进行运算
// 最终的运算结果,得到的其实是一个 Optional 类型,需要使用 get() 获取到里面的数据
int result4 = list.stream().reduce((e1, e2) -> e1 + e2).get();
System.out.println(result4);
3.4、count

​ 统计流中的元素数量。

long result5 = list.stream().count();
System.out.println(result5);
3.5、forEach

​ 迭代、遍历流中的数据。

list.stream().forEach(System.out::println);
3.6、max & min

​ 获取流中的最大的元素、最小的元素。

// 获取最大值
Integer result6 = list.stream().max(Integer::compareTo).get();
System.out.println("max is : " + result6);
// 获取最小值
Integer result7 = list.stream().min(Integer::compareTo).get();
System.out.println("min is : " + result7);
3.7、Matching

​ allMatch: 只有当流中所有的元素,都匹配指定的规则,才会返回 true

​ anyMatch: 只要流中有任意的数据,满足指定的规则,都会返回 true

​ noneMatch: 只有当流中的所有的元素,都不满足指定的规则,才会返回true

// 判断流中是否所有的元素都大于 50
boolean result8 = list.stream().allMatch(ele -> ele > 50);
System.out.println(result8);

// 判断流中是否有大于 50 的数据
boolean result9 = list.stream().anyMatch(ele -> ele > 50);
System.out.println(result9);

// 判断流中是否没有奇数
boolean result10 = list.stream().noneMatch(ele -> ele % 2 != 0);
System.out.println(result10);
3.8、find

​ findFirst: 从流中获取一个元素(一般情况下,是获取的开头的元素)

​ findAny: 从流中获取一个元素(一般情况下,是获取的开头的元素)

​ 这两个方法,绝大部分情况下,是完全相同的,但是在多线程的环境下,findAny和find返回的结果可能不一样。

Integer result11 = list.parallelStream().findFirst().get();
System.out.println(result11);

Integer result12 = list.parallelStream().findAny().get();
System.out.println(result12);
3.9、summaryStatistics(通常mapToInt配合使用)

根据此方法获取到集合中的最大值,最小值、平均值、数量以及求和等信息。

IntSummaryStatistics statistics = stream.mapToInt(o>o.getAge()).summaryStatistics();
        System.out.println("最大年龄: " + statistics.getMax());
        System.out.println("最小年龄: " + statistics.getMin());
        System.out.println("平均年龄: " + statistics.getAverage());
3.10、最终操作的注意事项

​ 最终操作,会关闭流。如果一个流被关闭了,再去使用这个流,就会出现异常。

// 9、最终操作错误示范
Stream<Integer> stream = list.stream();
long count = stream.count();
stream.forEach(System.out::println);
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at com.qf.cstream.FinalOperationDemo.main(FinalOperationDemo.java:78)

4、中间操作


4.1、中间操作的简介

​ 将数据从数据源中读取到流中,中间操作,就是对流中的数据进行各种各样的操作、处理。中间操作可以连续操作,每一个操作的返回值都是一个Stream对象,可以继续进行其他的操作。直到最终操作。

4.2、filter(一般与limit搭配使用)

​ 条件过滤,仅保留流中满足指定条件的数据,其他不满足的数据都会被删除。

list.stream().filter(ele -> ele.length() > 5).forEach(System.out::println);
4.3、distinct

​ 去重集合中重复的元素。这个方法没有参数。去重的规则与HashSet相同。

list.stream().distinct().forEach(System.out::println);
4.4、sorted

​ 将流中的数据进行排序

// 按照流中的元素对应的类,实现的 Comparable 接口中的方法实现排序
list.stream().sorted().forEach(System.out::println);
// 将流中的数据,按照指定的规则进行排序
list.stream().sorted((e1, e2) -> e1.length() - e2.length()).forEach(System.out::println);
4.5、limit & skip

​ limit: 限制,截取流中指定数量的元素

​ skip: 跳过,跳过流中的指定数量的元素

// 跳过开头的指定的元素
list.stream().skip(3).forEach(System.out::println);
// 截取指定数量的元素
list.stream().limit(3).forEach(System.out::println);
// 配合使用,截取部分
list.stream().skip(5).limit(2).forEach(System.out::println);
4.6、map & flatMap

​ map: 对流中的数据进行映射,用新的数据替换旧的数据。

list.stream().map(ele -> ele + ".txt").forEach(System.out::println);

​ map最主要,就是来做元素的替换。其实map是一个元素的映射。

​ flatMap也是元素的映射,使用场合:

​ 常用于map直接映射完成后,流中的数据是一个个的容器,而我们需要对容器中的元素进行处理,此时, 可以使用扁平化映射,将容器中的元素直接存入流中。

flatMap是扁平化映射,一般跟在map映射之后,例:

//统计集合里共有多少不重复字符
long count = Arrays.stream(list).map(list->list.split("")).flatMap(Arrays::stream).dintinct().count();

faltmap映射图示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9BweT4f9-1587779824840)(E:\文件夹目录\JAVA\课程基础\3.jpg)]

4.7、mapToInt

​ 将流中的数据替换成int类型,此时得到 IntStream 结果。

double avg = list.stream().mapToInt(Person::getAge).average().getAsDouble();
//.average:求平均
//.sum:求和
//.min:最小值
//.max:最大值
System.out.println(avg);

5、Collectors工具类


​ Collectors是一个工具类,里面封装了很多方法,可以很方便的获取到一个Collector接口的实现类对象,从而可以使用collect()方法,对流中的数据,进行各种各样的处理、整合。

Collectors.toList()	: 将流中的数据,聚合到一个 List 集合中
Collectors.toSet()	: 将流中的数据,聚合到一个 Set 集合中
Collectors.toMap()	: 将流中的数据,聚合到一个 Map 集合中

maxBy()				: 按照指定的规则,找到流中最大的元素,等同于 max
minBy()				: 按照指定的规则,找到流中最小的元素,等同于 min

joining()			: 将流中的数据拼接成一个字符串,注意:只能操作流中是String的数据

summingInt()		: 将流中的数据,映射成 int 类型的数据,并求和
averagingInt()		: 将流中的数据,映射成 int 类型的数据,并求平均值
summarizingInt()	: 将流中的数据,映射成 int 类型的数据,并获取描述信息
// maxBy: 按照指定的规则,找到流中最大的元素,等同于 max
Student max = list.stream()
                  .collect(Collectors.maxBy((s1, s2) -> s1.getScore() - s2.getScore()))
                  .get();
System.out.println(max);

// minBy: 按照指定的规则,找到流中最小的元素,等同于 min
Student min = list.stream()
                  .collect(Collectors.minBy((s1, s2) -> s1.getScore() - s2.getScore()))
                  .get();
System.out.println(min);

// 将流中的数据拼接起来,以指定的分隔符进行分隔(3参时,后2个代表添加前缀和尾缀,无参代表直接拼接)
//只有当流中的数据是字符串的时候才能进行拼接
String s3 = list.stream()
                .map(Student::getName)
                .collect(Collectors.joining(", ", "{", "}"));
System.out.println(s3);

// 将流中的数据,映射成 int 类型的数据,并求和
int sum = list.stream()
              .collect(Collectors.summingInt(
                  非静态时:(lambda表达式)o->o.getScore()
                  静态方法时:(函数引用)Student::getScore
              ));
System.out.println(sum);

// 将流中的数据,映射成 int 类型的数据,并求平均值
double average = list.stream()
                     .collect(Collectors.averagingInt(Student::getScore));
System.out.println(average);

// 将流中的数据,映射成 int 类型的数据,并获取描述信息
IntSummaryStatistics summaryStatistics 
                = list.stream()
                      .collect(Collectors.summarizingInt(Student::getScore));
System.out.println(summaryStatistics);
System.out.println(summaryStatistics.getCount());
System.out.println(summaryStatistics.getSum());
System.out.println(summaryStatistics.getMax());
System.out.println(summaryStatistics.getMin());
System.out.println(summaryStatistics.getAverage());

6、综合案例


需求: 一个集合中存储了了若干个Student对象, 要求查询出以下结果:
1. 所有及格的学生信息
2. 所有及格的学生姓名
3. 所有学生的平均成绩
4. 班级的前3(按照成绩)
5. 班级的3-10(按照成绩)
6. 所有不不及格的学生平均成绩
7. 将及格的学生, 按照成绩降序输出所有信息 
8. 班级学生的总分
public class Student {
    private String name;
    private int score;
    
    public String getName() {
        return name;
    }
    
    public int getScore() {
        return score;
    }
    
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    
    @Override
    public String toString() {
        return String.format("姓名: %s, 成绩: %d", name, score);
    }
}

你可能感兴趣的:(Java8新特性之集合的流式编程)