Stream:常用方法总结

Stream

forEach

Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

Random random = new Random(); 
random.ints().limit(10).forEach(System.out::println);

map

map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); 
// 获取对应的平方数 
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

flatMap

将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流

List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");
 
list = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(toList());

上面例子中,我们的目的是把 List 中每个字符串元素以" "分割开,变成一个新的 List。
首先 map 方法分割每个字符串元素,但此时流的类型为 Stream<String[ ]>,因为 split 方法返回的是 String[ ] 类型;所以我们需要使用 flatMap 方法,先使用Arrays::stream将每个 String[ ] 元素变成一个 Stream 流,然后 flatMap 会将每一个流连接成为一个流,最终返回我们需要的 Stream


filter

filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 
// 获取空字符串的数量 
long count = strings.stream().filter(string -> string.isEmpty()).count();

skip

先从集合中截取5个元素,然后取后3个

//从集合第三个开始截取5个数据
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
List<Integer> collect = integers.stream().skip(3).limit(5).collect(Collectors.toList());
collect.forEach(integer -> System.out.print(integer+" "));

limit

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

Random random = new Random(); 
random.ints().limit(10).forEach(System.out::println);

sorted

如果流中的元素的类实现了 Comparable 接口,即有自己的排序规则,那么可以直接调用 sorted() 方法对元素进行排序,如 Stream,反之, 需要调用 sorted((T, T) -> int) 实现 Comparator 接口

// 根据年龄大小来比较:
list = list.stream()
           .sorted((p1, p2) -> p1.getAge() - p2.getAge())
           .collect(toList());

当然这个可以简化为,推荐使用写法简单明了

list = list.stream()
           .sorted(Comparator.comparingInt(Person::getAge))
           .collect(toList());

案例

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

Random random = new Random(); 
random.ints().limit(10).sorted().forEach(System.out::println);

并行(parallel)程序

parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 
// 获取空字符串的数量 
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();

我们可以很容易的在顺序运行和并行直接切换。


收集器Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

counting

long l = list.stream().collect(counting());
long l = list.stream().count();

推荐第二种

summingInt ,summingLong ,summingDouble

summing,没错,也是计算总和,不过这里需要一个函数参数

计算 Person 年龄总和:

int sum = list.stream().collect(summingInt(Person::getAge));
int sum = list.stream().mapToInt(Person::getAge).sum();
int sum = list.stream().map(Person::getAge).reduce(Interger::sum).get();

推荐第二种

averagingInt,averagingLong,averagingDouble

Double average = list.stream().collect(averagingInt(Person::getAge));
OptionalDouble average = list.stream().mapToInt(Person::getAge).average();

summarizingInt,summarizingLong,summarizingDouble

这三个方法比较特殊,比如 summarizingInt 会返回 IntSummaryStatistics 类型

IntSummaryStatistics collect6 = students.stream().collect(Collectors.summarizingInt(Student::getAge));
double average = collect6.getAverage();
long sum = collect6.getSum();
long count1 = collect6.getCount();
int max1 = collect6.getMax();
int min1 = collect6.getMin();

IntSummaryStatistics 包含了计算出来的平均值,总数,总和,最值

取最值

maxBy,minBy 两个方法,需要一个 Comparator 接口作为参数

Optional<Person> optional = list.stream().collect(maxBy(comparing(Person::getAge)));
Optional<Person> optional = list.stream().max(comparing(Person::getAge));

joining 连接字符串

也是一个比较常用的方法,对流里面的字符串元素进行连接,其底层实现用的是专门用于字符串连接的 StringBuilder

String s = list.stream().map(Person::getName).collect(joining());
 
结果:jackmiketom
String s = list.stream().map(Person::getName).collect(joining(","));
 
结果:jack,mike,tom

joining 还有一个比较特别的重载方法:

String s = list.stream().map(Person::getName).collect(joining(" and ", "Today ", " play games."));
 
结果:Today jack and mike and tom play games.

即 Today 放开头,play games. 放结尾,and 在中间连接各个字符串

用于计算总和:

ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,19,"张三","M",true));
students.add(new Student(1,18,"李四","M",false));
students.add(new Student(1,21,"王五","F",true));
students.add(new Student(1,20,"赵六","F",false));

//通过counting()统计集合总数  方法一
Long collect = students.stream().collect(Collectors.counting());
System.out.println(collect);
//结果 4

//通过count()统计集合总数  方法二
long count = students.stream().count();
System.out.println(count);
//结果 4

//通过maxBy求最大值
Optional<Student> collect1 = students.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)));
if(collect1.isPresent()){
    System.out.println(collect1);
}
//结果 Optional[Student{id=1, age=21, name='王五', sex='F', isPass=true}]

//通过max求最大值
Optional<Student> max = students.stream().max(Comparator.comparing(Student::getAge));
if(max.isPresent()){
    System.out.println(max);
}
//结果  Optional[Student{id=1, age=21, name='王五', sex='F', isPass=true}]

//通过minBy求最小值
Optional<Student> collect2 = students.stream().collect(Collectors.minBy(Comparator.comparing(Student::getAge)));
if(collect2.isPresent()){
    System.out.println(collect2);
}
//结果  Optional[Student{id=1, age=18, name='李四', sex='M', isPass=false}]

//通过min求最小值
Optional<Student> min = students.stream().min(Comparator.comparing(Student::getAge));
if(min.isPresent()){
    System.out.println(min);
}
//结果  Optional[Student{id=1, age=18, name='李四', sex='M', isPass=false}]

//通过summingInt()进行数据汇总
Integer collect3 = students.stream().collect(Collectors.summingInt(Student::getAge));
System.out.println(collect3);

//通过averagingInt()进行平均值获取
Double collect4 = students.stream().collect(Collectors.averagingInt(Student::getAge));
System.out.println(collect4);
//结果 19.5

//通过joining()进行数据拼接
String collect5 = students.stream().map(Student::getName).collect(Collectors.joining());
System.out.println(collect5);
//结果 张三李四王五赵六

//复杂结果的返回


统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);  
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();  
System.out.println("列表中最大的数 : " + stats.getMax()); 
System.out.println("列表中最小的数 : " + stats.getMin()); 
System.out.println("所有数之和 : " + stats.getSum()); 
System.out.println("平均数 : " + stats.getAverage());

anyMatch

anyMatch()主要用于判断流中是否至少存在一个符合条件的元素,它会返回一个boolean值,并且对于它的操作, 一般叫做短路求值

案例:判断集合中是否有年龄小于20的学生

//判断集合中是否有年龄小于20的学生
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,19,"张三","M",true));
students.add(new Student(1,18,"李四","M",false));
students.add(new Student(1,21,"王五","F",true));
students.add(new Student(1,20,"赵六","F",false));

if(students.stream().anyMatch(student -> student.getAge() < 20)){
    System.out.println("集合中有年龄小于20的学生");
}else {
    System.out.println("集合中没有年龄小于20的学生");
}

根据上述例子可以看到,当流中只要有一个符合条件的元素,则会立刻中止后续的操作,立即返回一个布尔值,无需遍历整个流。


allMatch

allMatch()的工作原理与anyMatch()类似,但是anyMatch执行时,只要流中有一个元素符合条件就会返回true, 而allMatch会判断流中是否所有条件都符合条件,全部符合才会返回true

案例:判断集合所有学生的年龄是否都小于20

//判断集合所有学生的年龄是否都小于20
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,19,"张三","M",true));
students.add(new Student(1,18,"李四","M",false));
students.add(new Student(1,21,"王五","F",true));
students.add(new Student(1,20,"赵六","F",false));

if(students.stream().allMatch(student -> student.getAge() < 20)){
    System.out.println("集合所有学生的年龄都小于20");
}else {
    System.out.println("集合中有年龄大于20的学生");
}

findFirst

findFirst用于获取流中的 第一个元素。

findAny

案例:findAny用于获取流中随机的某一个元素,并且利用短路在找到结果时,立即结束

//findAny用于获取流中随机的某一个元素,并且利用短路在找到结果时,立即结束
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,19,"张三1","M",true));
students.add(new Student(1,18,"张三2","M",false));
students.add(new Student(1,21,"张三3","F",true));
students.add(new Student(1,20,"张三4","F",false));
students.add(new Student(1,20,"张三5","F",false));
students.add(new Student(1,20,"张三6","F",false));
Optional<Student> student1 = students.stream().filter(student -> student.getSex().equals("F")).findAny();
System.out.println(student1.toString());

结果

Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]

此时我们将其循环100次

//findAny用于获取流中随机的某一个元素,并且利用短路在找到结果时,立即结束
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,19,"张三1","M",true));
students.add(new Student(1,18,"张三2","M",false));
students.add(new Student(1,21,"张三3","F",true));
students.add(new Student(1,20,"张三4","F",false));
students.add(new Student(1,20,"张三5","F",false));
students.add(new Student(1,20,"张三6","F",false));
for (int i = 0; i < 100; i++) {
    Optional<Student> student1 = students.stream().filter(student -> student.getSex().equals("F")).findAny();
    System.out.println(student1.toString());
}

结果:

Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]
Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]
Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]
Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]
Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]
...
Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]

这时候我们改为并行流在执行一下

//findAny用于获取流中随机的某一个元素,并且利用短路在找到结果时,立即结束
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,19,"张三1","M",true));
students.add(new Student(1,18,"张三2","M",false));
students.add(new Student(1,21,"张三3","F",true));
students.add(new Student(1,20,"张三4","F",false));
students.add(new Student(1,20,"张三5","F",false));
students.add(new Student(1,20,"张三6","F",false));
for (int i = 0; i < 100; i++) {
    Optional<Student> student1 = students.parallelStream().filter(student -> student.getSex().equals("F")).findAny();
    System.out.println(student1.toString());
}

结果

Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]
Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]
Optional[Student{id=1, age=20, name='张三4', sex='F', isPass=true}]
Optional[Student{id=1, age=20, name='张三4', sex='F', isPass=true}]
Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]
...
Optional[Student{id=1, age=21, name='张三3', sex='F', isPass=true}]

总结:当为串行流且数据较少时,获取的结果一般为流中第一个元素,但是当为并流行的时 候,则会随机获取。


reduce

累积求和

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
Integer reduce = integers.stream().reduce(0, (integer1, integer2) -> integer1 + integer2);
System.out.println(reduce);

优化

Integer reduce = integers.stream().reduce(0,Integer::sum);

不带初始值参数的重载方法

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
Optional<Integer> reduce = integers.stream().reduce(Integer::sum);
if(reduce.isPresent()){
    System.out.println(reduce);
}else {
    System.out.println("数据有误");
}

最大值、最小值

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);

/**
 * 获取集合中的最大值
 */

//方法一
Optional<Integer> max1 = integers.stream().reduce(Integer::max);
if(max1.isPresent()){
    System.out.println(max1);
}

//方法二
Optional<Integer> max2 = integers.stream().max(Integer::compareTo);
if(max2.isPresent()){
    System.out.println(max2);
}

/**
 * 获取集合中的最小值
 */

//方法一 
Optional<Integer> min1 = integers.stream().reduce(Integer::min);
if(min1.isPresent()){
    System.out.println(min1);
}

//方法二
Optional<Integer> min2 = integers.stream().min(Integer::compareTo);
if(min2.isPresent()){
    System.out.println(min2);
}

结果:

Optional[8]
Optional[8]
Optional[1]
Optional[1]

分组

//通过性别对学生进行分组
Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(Student::getSex));

结果

{
	F=[Student{id=1, age=21, name='王五', sex='F', isPass=true}, Student{id=1, age=20, name='赵六', sex='F', isPass=false}],
	M=[Student{id=1, age=19, name='张三', sex='M', isPass=true}, Student{id=1, age=18, name='李四', sex='M', isPass=false}]
}

多级分组

//现根据是否通过考试对学生分组,在根据性别分组     
Map<String, Map<Boolean, List<Student>>> collect1 = students.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getPass)));

结果

{
	F={
        false=[Student{id=1, age=20, name='赵六', sex='F', isPass=false}],
        true=[Student{id=1, age=21, name='王五', sex='F', isPass=true}]
    }, 
    M={
        false=[Student{id=1, age=18, name='李四', sex='M', isPass=false}], 
        true=[Student{id=1, age=19, name='张三', sex='M', isPass=true}]}
}

按组收集数据

Map<Integer, Integer> map = list.stream().collect(groupingBy(Person::getAge, summingInt(Person::getAge)));

该例子中,我们通过年龄进行分组,然后 summingInt(Person::getAge)) 分别计算每一组的年龄总和(Integer),最终返回一个 Map<Integer, Integer>

多级分组变形

在日常开发中,我们很有可能不是需要返回一个数据集合,还有可能对数据进行汇总操作,比方说对于年龄18岁 的通过的有多少人,未及格的有多少人。因此,对于二级分组收集器传递给外层分组收集器的可以任意数据类型, 而不一定是它的数据集合。

//根据年龄进行分组,获取并汇总人数
Map<Integer, Long> collect2 = students.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.counting()));
System.out.println(collect2)

结果

{18=1, 19=1, 20=1, 21=1}
//要根据年龄与是否及格进行分组,并获取每组中年龄的学生
Map<Integer, Map<Boolean, Student>> collect3 = students.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.groupingBy(Student::getPass,                                                                             Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Student::getAge)), Optional::get))));

System.out.println(collect3.toString());

结果

{
	18={false=Student{id=1, age=18, name='李四', sex='M', isPass=false}},
    19={true=Student{id=1, age=19, name='张三', sex='M', isPass=true}},
    20={false=Student{id=1, age=20, name='赵六', sex='F', isPass=false}}, 
    21={true=Student{id=1, age=21, name='王五', sex='F', isPass=true}}
}

partitioningBy 分区

根据年龄是否小于等于20来分区
Map<Boolean, List<Person>> map = list.stream()
                                     .collect(partitioningBy(p -> p.getAge() <= 20));
 
打印输出
{
    false=[Person{name='mike', age=25}, Person{name='tom', age=30}], 
    true=[Person{name='jack', age=20}]
}

同样地 partitioningBy 也可以添加一个收集器作为第二参数,进行类似 groupBy 的多重分区等等操作。

数值流-和,平均,最值推荐使用

int sum = list.stream().map(Person::getAge).reduce(0, Integer::sum); 计算元素总和的方法其中暗含了装箱成本,map(Person::getAge) 方法过后流变成了 Stream 类型,而每个 Integer 都要拆箱成一个原始类型再进行 sum 方法求和,这样大大影响了效率。

针对这个问题 Java 8 有良心地引入了数值流 IntStream, DoubleStream, LongStream,这种流中的元素都是原始数据类型,分别是 int,double,long

流转换为数值流

  • mapToInt(T -> int) : return IntStream
  • mapToDouble(T -> double) : return DoubleStream
  • mapToLong(T -> long) : return LongStream
IntStream intStream = list.stream().mapToInt(Person::getAge);

当然如果是下面这样便会出错

LongStream longStream = list.stream().mapToInt(Person::getAge);

因为 getAge 方法返回的是 int 类型(返回的如果是 Integer,一样可以转换为 IntStream)

数值流转换为流

很简单,就一个 boxed

Stream<Integer> stream = intStream.boxed();

数值流方法

sum()
max()
min()
average() 等…

数值范围
IntStream 与 LongStream 拥有 range 和 rangeClosed 方法用于数值范围处理

IntStream : rangeClosed(int, int) / range(int, int) LongStream : rangeClosed(long, long) / range(long, long)

我们可以利用 IntStream.rangeClosed(1, 100) 生成 1 到 100 的数值流

// 求 1 到 10 的数值总和:
IntStream intStream = IntStream.rangeClosed(1, 10);
int sum = intStream.sum();

这两个方法的区别在于一个是闭区间,一个是半开半闭区间:

rangeClosed(1, 100) :[1, 100]
range(1, 100) :[1, 100)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
JavaStream流的常用方法包括获取Stream流、中间方法和终结方法。 获取Stream流的方法有两种:集合获取Stream流和数组获取Stream流。集合获取Stream流可以使用集合类的stream()方法,例如Map的keySet()方法可以获取键流,values()方法可以获取值流,entrySet()方法可以获取键值对流。数组获取Stream流可以使用Arrays类的stream()方法,将数组转换为Stream流。 中间方法是对Stream流进行操作的方法,一次操作完毕之后,还可以继续进行其他操作。常用的中间方法包括filter()、map()、flatMap()、distinct()、sorted()、limit()和skip()等。filter()方法用于过滤元素,map()方法用于对元素进行映射,flatMap()方法用于扁平化处理,distinct()方法用于去重,sorted()方法用于排序,limit()方法用于限制元素数量,skip()方法用于跳过元素。 终结方法Stream流的最后一个操作,一个Stream流只能有一个终结方法常用的终结方法包括forEach()、collect()、count()、min()、max()、reduce()和toArray()等。forEach()方法用于遍历元素,collect()方法用于将元素收集到集合中,count()方法用于统计元素数量,min()方法用于获取最小值,max()方法用于获取最大值,reduce()方法用于对元素进行归约操作,toArray()方法用于将元素转换为数组。 综合应用Stream流的常用方法可以实现对数据的筛选、转换、排序、统计等操作,提高代码的简洁性和可读性。 #### 引用[.reference_title] - *1* *2* *3* [Java 基础进阶篇(十二):Stream常用方法总结](https://blog.csdn.net/weixin_43819566/article/details/130537949)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员无羡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值