内容纲要

这是在入职时的一个学习笔记,总结了Java8新特性stream的使用场景和方式。经过一段时间的工作使用后,觉得stream的写法逻辑清晰而且代码整洁。所以重新整理了一下笔记,并将其放在个人网站上留档。

简介

Stream是Java8的新特性,主要是用于计算并产生一个结果的操作。

概念

stream表示元素的序列,同时可以对该序列进行各种操作。

这些操作分为:

  • 中间操作(Intermediate)

    • 有状态:该操作处理元素不受操作前元素的影响

    • 无状态:该操作只有在拿到所有元素后才能继续

  • 终端操作(Terminal)

    • 非短路:只有对所有数据都进行操作后才能得到最后结果
    • 短路:只要得到符合条件的某个数据就可以返回结果,如findAny

特性

  1. 不会保存数据
  2. 不修改原来的数据(除了peek,但peek推荐使用于debug中所以可以不做考虑)
  3. 惰性的操作,只有当访问到流中的一个元素时,才会进行一系列操作

创建stream

  • 对List和Set进行stream()和parralleStream()操作
  • Stream.of()
  • 对于int,long和double类型的数据,可以采用IntStream LongStream DoubleStream

注:对于Map之类的数据结构也可以同样适用stream。对Map进行.entrySet()操作后可以对Set的数据结构进行stream操作。例如:

Map<String, Integer> nameToAgeMap = Map.of("Tom", 25, "Alice", 22, "Tony", 23);
nameToAgeMap.entrySet()
  .stream()
  .filter(entry -> entry.getValue() > 18)
  .foreach(entry -> System.out.println(entry.getKey() + entry.getValue()))

中间操作

过滤/筛选

  • filter

作用:对元素进行过滤,符合条件的筛选出来,不符合条件过滤出去

传参:一个predicate的函数接口,该接口传入泛型后返回一个boolean

用法:对于每一个元素都进行参数中的操作,当返回的布尔值为true后,该元素才会进行filter后的操作,返回的布尔值为false后该元素被过滤掉。

Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8);
Stream<Integer> oddStream = stream.filter(number -> number%2==1);      //1,3,5,7
  • limit

作用:只保留前n个元素

传参:n 保留的元素个数

用法:对于元素流取固定个数的元素,截取前n个元素,过滤掉不需要的元素

Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8);
Stream<Integer> halfStream = stream.limit(4);         //1,2,3,4
  • skip

作用:跳过前n个元素

传参:n 保留的元素个数

用法:对于元素流忽略固定个数的元素,过滤掉前n个元素,只取其后的元素

Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8);
Stream<Integer> halfStream = stream.skip(4);          //5,6,7,8
  • distinct

作用:去重复

传参:无

用法:对于一个数据流,删除其中重复的元素

Stream<Integer> stream = Stream.of(1,1,2,2,3,3,4,4);
Stream<Integer> halfStream = stream.distinct();           //1,2,3,4

映射

  • map

作用:转换元素的值,或者对元素进行某种操作,返回一个其他的值

传参:function函数接口

用法:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素

List<String> list = Arrays.asList("a,b,c", "1,2,3");

//将每个元素转成一个新的且不带逗号的元素,list中的元素是"a,b,c"和"1,2,3"
Stream<String> newList = list.stream().map(s -> s.replaceAll(",", ""));        // abc  123
  • flatMap

作用:转换多个元素流的值,返回一个元素流

传参:function函数接口

用法:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

List<String> list = Arrays.asList("a,b,c", "1,2,3");

Stream<String> newList = list.stream().flatMap(s -> {
    //将每个元素转换成一个stream
    String[] split = s.split(",");
    Stream<String> s2 = Arrays.stream(split);
    return s2;
});         // a b c 1 2 3
  • mapToXXX

作用:将元素转化成对应的类型,元素流转化为基本类型流,对应int long double object类型

传参:无

用法:mapToInt() mapToLong() mapToDouble() mapToObj()

Stream.of(1.0, 2.0, 3.0)
    .mapToInt(Double::intValue)             //1,2,3
    .mapToObj(i -> "a" + i)                        //a1,a2,a3
    .forEach(System.out::println);
IntStream.range(1, 4)                                   //1,2,3
    .mapToObj(i -> "a" + i)                        //a1,a2,a3
    .forEach(System.out::println);
  • peek

作用:功能与map相似,但peek没有返回值,会对原元素流进行修改

传参:costomre函数

用法:peek 操作会按照 Costomer 函数提供的逻辑去消费流中的每一个元素,可能改变元素内部的一些属性,costomer函数的返回值为void,主要用于debug用途

Stream.of("one", "two", "three","four").filter(e -> e.length() > 3)
                .peek(e -> System.out.println("Filtered value: " + e))
  //Filtered value: three       Filtered value: four
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: " + e))
  //Mapped value:Three      Mapped value:Four
                .collect(Collectors.toList());

总结:这样的话,可以输出中间操作后的元素流中的每个元素值,主要用于debug

Student s1 = new Student("name1", 18);
Student s2 = new Student("name2", 20);
List<Student> studentList = Arrays.asList(s1, s2);

studentList.stream()
        .peek(student -> student.setAge(100))
        .forEach(System.out::println);   //{name='aa', age=100},{name='bb', age=100}   

排序

  • sorted

作用:对元素流进行排序

传参:无(排序) Comparator.comparing(根据属性排序)

用法:当不传入任何参数时,进行排序,默认升序,若需要降序使用.reserved();若是对某个对象排序,传入参数使得可以按照对象的某个属性进行排序

Stream<Integer> stream = Stream.of(2,4,3,5,1,8,7,6);
Stream<Integer> sortedSream = stream.sorted();            //1,2,3,4,5,6,7,8
Stream<Integer> reversedSortedStream = stream.sorted().reversed();    //8,7,6,5,4,3,2,1

对于按照某个属性进行降序排序的方法,还有Comparator.reverseOrder()的方法

stream.sorted(Comparator.comparing(User::name,Comparator.reverseOrder()))       //todo
  //对于User中的name进行降序排序

终端操作

匹配

  • allMatch

作用:判断元素流中的元素是否符合Predicate函数

传参:Predicate 函数

返回值:boolean

用法:当流中每个元素都符合该断言时才返回true,否则返回false

Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8);
boolean result = stream.allMatch(element -> element%2==0);           //false
boolean result2 = stream.allMatch(element -> element > 0);            //true
  • noneMatch

作用:判断元素流中的元素是否都不符合Predicate函数

传参:Predicate 函数

返回值:boolean

用法:当流中每个元素都不符合该断言时才返回true,否则返回false

Stream<Integer> stream = Stream.of(1,3,5,7,9,11);
boolean result = stream.noneMatch(element -> element%2==0);          //true
boolean result2 = stream.noneMatch(element -> element > 10);          //false
  • anyMatch

作用:判断元素流中的元素是否有符合Predicate函数的元素

传参:Predicate 函数

返回值:boolean

用法:只要流中有一个元素符合该断言就会返回true,否则返回false

Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,11);
boolean result = stream.anyMatch(element -> element%2==0);           //true
boolean result2 = stream.anyMatch(element -> element > 10);           //true
  • findFirst

作用:取出元素流中的第一个元素

返回值:Optional

用法:一般配合filter使用,返回filter过滤后流中第一个元素

List<Integer> list = Arrays.asList(2, 3, 4, 5);

Optional<Integer> findOdd = list.stream()
  .filter(element -> element%2==1).findFirst(); //3
  • findAny

作用:取出元素流中的任意元素(适用于并行流parallelStream)

返回值:Optional

用法:在串行的流中,findAny和findFirst返回的,都是第一个元素;而在并行的流中,findAny返回的是最快处理完的那个线程的元素,不一定是第一个元素

List<Integer> list = Arrays.asList(2, 3, 4, 5);

Optional<Integer> findOdd = list.stream()
  .filter(element -> element%2==1).findAny(); //3

统计/计算

  • count

作用:统计元素流中元素的个数

返回值:long

List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);

long count = list.stream().filter(x -> x > 6).count();            //4
  • max

作用:统计元素流中元素最大值

传参:无/Comparator

返回值:Optional

List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);

Optional<Integer> max = list.stream().max(Integer::compareTo);        //11
  • min

作用:统计元素流中元素最小值

传参:无/Comparator

返回值:Optional

List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);

Optional<Integer> min = list.stream().min(Integer::compareTo);        //6
List<String> list = Arrays.asList("7", "6", "9", "4", "11", "6");

OptionalInt min = list.stream().mapToInt(Integer::parseInt).min();  //6
  • reduce

作用:把元素流缩减成一个值,实现求和/乘积等一系列计算操作

传参:function

返回值:Optional

List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 5);
// 求和
Optional<Integer> sum2 = list.stream().reduce(Integer::sum);          //30
List<Integer> list = Arrays.asList(1, 3, 4, 8, 11, 2);
// 求乘积
Optional<Integer> product = list.stream().reduce((x, y) -> x * y); //2112
List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
// 求最大值
Integer max2 = list.stream().reduce(1, Integer::max);                                       //11
//BigDecimal求和方式
BigDecimal element1 = new BigDecimal(5);
BigDecimal element2 = new BigDecimal(8);
List<BigDecimal> list = new ArrayList<>();
list.add(element1);
list.add(element2);
BigDecimal sum = list.stream().reduce(ZERO, BigDecimal::add);

收集

  • iterator

作用:获取指向元素流开始的迭代器

返回值:Iterator

用法:将元素流转化成迭代器,就可以使用迭代器的方法

List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
// 求最大值
Iterator<Integer> iterator = list.stream().iterator();

while (iterator.hasNext()) {
  System.out.println(iterator.next());      //1,3,2,8,11,4
}
  • forEach

作用:对元素流中的每一个元素进行处理

返回值:Customer

用法:根据传入的Customer函数,处理元素流中的每一个元素,在并行元素流中无法保持元素流顺序

List<String> strs = Arrays.asList("A", "B", "C", "D");
strs.stream().forEach(System.out::print);                       //ABCD
strs.parallelStream().forEach(System.out::print);       //不一定是ABCD
  • forEachOrdered

作用:对元素流中的每一个元素进行处理

返回值:Customer

用法:根据传入的Customer函数,处理元素流中的每一个元素,在并行元素流中依然会保持元素流顺序

List<String> strs = Arrays.asList("A", "B", "C", "D");
strs.stream().forEach(System.out::print);                       //ABCD
strs.parallelStream().forEach(System.out::print);       //ABCD
  • toArray

作用:将元素流中的元素转化成Array,返回的是基本类型的数组

返回值:Object[]

List<String> strs = Arrays.asList("a", "b", "c");
Object[] obj = strs.stream().toArray();
  • collect

作用:收集器操作

返回值:Collector(关于Collector在后续详细说明)

//常用
List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 5);
// 求和
List<Integer> oddList = list.stream()
  .filter(element -> element%2==1)
  .collect(Collectors.toList());

Collector收集器

  • Collectors.toList()/Collectors.toSet()/Collectors.toMap()

作用:在元素流进行一定的处理和操作后,将元素流中的元素重新归拢到新的集合中,包括list/set/map

传参:Collectors.toMap()中,第一个参数为keyMapper,第二个参数为valueMapper

List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
List<Integer> oddList = list.stream()
  .filter(x -> x % 2 == 1)
  .collect(Collectors.toList());
Set<Integer> set = list.stream()
  .filter(x -> x % 2 == 0)
  .collect(Collectors.toSet());
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom",  23, "male"));
personList.add(new Person("Jack", 25, "male"));
personList.add(new Person("Lily", 21, "female"));
personList.add(new Person("Anni", 24, "female"));

Map<String, Person> map = personList.stream()
  .collect(Collectors.toMap(Person::getName, p -> p));
  • Collectors.joining

作用:对元素流中的元素进行连接,支持指定的连接符

传参:无/连接符

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom",  23, "male"));
personList.add(new Person("Jack", 25, "male"));
personList.add(new Person("Lily", 21, "female"));
personList.add(new Person("Anni", 24, "female"));

String names = personList.stream()
  .map(Person::getName)
  .collect(Collectors.joining("-"));
  • Collectors.groupingBy()分组/Collectors.partitioningBy()分区

分组:根据分组的类别,将集合分成多个map

分区:根据分区条件,将stream按条件分成两部分

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom",  23, "male"));
personList.add(new Person("Jack", 21, "male"));
personList.add(new Person("Lily", 17, "female"));
personList.add(new Person("Anni", 26, "female"));

//根据是否成年进行分区
Map<Boolean, List<Person>> part = personList.stream()
  .collect(Collectors.partitioningBy(person -> person.getAge > 18));
//根据性别进行分组
Map<String, List<Person>> group = personList.stream()
  .collect(Collectors.groupingBy(Person::getSex));
  • 数据统计
    • 计数:count
    • 平均值:averagingInt/averagingLong/averagingDoubleƒ
    • 最值: /minBy
    • 求和:summingInt/summingLong/summingDouble
    • 统计以上所有:summarizingInt/summarizingLong/summarizingDouble
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom",  23, "male"));
personList.add(new Person("Jack", 22, "male"));
personList.add(new Person("Lily", 17, "female"));
personList.add(new Person("Anni", 26, "female"));

Long count = personList.stream().collect(Collectors.counting());        //4
IntSummaryStatistics collect = personList.stream()
  .collect(Collectors.summarizingInt(Person::getAge));
//{count=4, sum=88,min=17, average=22, max=26}

​ 上述这个操作同样用reduce也可以实现,写法为:

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom",  23, "male"));
personList.add(new Person("Jack", 22, "male"));
personList.add(new Person("Lily", 17, "female"));
personList.add(new Person("Anni", 26, "female"));

Optional<Integer> sum = personList.stream().map(Person::getAge).reduce(Integer::sum);
最后修改日期: 2024年1月5日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。