JDK8系列之Stream API入门教程和示例

2021-07-21 14:43:09 浏览数 (1)

JDK8系列之Stream API入门教程和示例

在前面的章节的学习中,我们学习了jdk8的新特性,lambada表达式、方法引用、函数式接口等等,接着本博客继续JDK8的一个比较重要的特性,JDK8 Stream API

1、什么是Jdk8 Stream?

Stream api是jdk8的新特性,使用jdk中java.util.stream里库,这种风格将元素集合看作一种stream,stream在管道中传输,在管道节点经过筛选、排序、聚合等操作,然后由terminal操作得到结果

2、Jdk8 Stream优点是什么?

  • Java 8 中的 Stream 是对集合(Collection)对象功能的增强,Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码
  • 同时Jdk8 Stream api提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势

  • Stream stream() : 返回一个顺序流
  • Stream parallelStream() : 返回一个并行流

3、Jdk8 Stream的特征

  • 不存储数据:Stream数据来源于数据源,但是本身是不存储数据元素的,而是将管道的数据元素传递给操作
  • 函数式编程:流的操作不会修改数据
  • 延迟操作:流的操作,如Filter,map等中间操作是可以延迟的,只有到terminal操作才会将操作顺序执行
  • 可以解绑:stream api有些操作是要求在有限的时间完成的,比如limit(n) 或 findFirst(),这些操作访问到有限的元素后就可以返回
  • 一次性消费:流的元素只能访问一次,如果你想重新访问流的元素,你得重新生成一个新的流。

4、Stream流的所有操作函数

  • 中间操作(intermediate operation)
    • 无状态(Stateless)
      • unordered()
      • filter()
      • map()
      • mapToInt()
      • mapToLong()
      • mapToDouble
      • flatMap()
      • flatMapToInt()
      • flatMapToLong()
      • flatMapToDouble()
      • peek()
    • 有状态(Stateful)
      • distinct()
      • sorted()
      • limit()
      • skip()
  • terminal 操作(terminal operation)
    • 非短路操作
      • forEach()
      • forEachOrdered()
      • toArray()
      • reduce()
      • collect()
      • max()
      • min()
      • count()
    • 短路操作(short-circuiting)
      • anyMatch()
      • allMatch()
      • noneMatch()
      • findFirst()
      • findAny()

5、Stream流的创建方式

  • 由集合创建Stream
代码语言:javascript复制
new ArrayList<>().stream();
  • 由数组创建Stream
代码语言:javascript复制
Arrays.stream(new int[]{1,2,3})
  • 由值创建Stream
代码语言:javascript复制
Stream<Integer> integerStream = Stream.of(1);
  • 由文件创建Stream
代码语言:javascript复制
// example : 从文件读取数据
BufferedReader bufferedReader = null;
try {
    bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("D://javap.txt")));
} catch (FileNotFoundException e) {
    // ignore exception
    log.error("FileNotFoundException :{}",e);
}
Stream<String> lines = bufferedReader.lines();
lines.forEach(s -> {System.out.println(s);});

读取jar文件

代码语言:javascript复制
 Stream<JarEntry> stream = new JarFile("").stream();

6、 Stream有限流和无限流

代码语言:javascript复制
// example:创建有限流
IntStream.of(new int[]{1,2,3});
IntStream.range(1,10);
IntStream.rangeClosed(1,10);
//使用Pattern 将字符串分隔成流
Pattern pattern = compile(",");
Stream<String> streams = pattern.splitAsStream("a , b , c , d , e");
streams.forEach( System.out::print);
代码语言:javascript复制
 // example :创建无限流
// 无限等比数列
 Stream<Integer> columns = Stream.iterate(1 , n -> n*2);
 // 生成无限随机数流
 Stream<Double> nums = Stream.generate(Math::random);
 // 无限数值流
 IntStream ints = new Random().ints();

7、intermediate operations

intermediate operations,又被称之为中间操作,中间操作会返回一个新的流,并且操作是延迟执行的,它不会修改原始的数据源,而且是由在终点操作开始的时候才真正开始执行

  • distinct 唯一
代码语言:javascript复制
 // example :distinct 唯一
 List<String> distinctStrs = Stream.of("a", "b" , "c" , "d" , "e", "b")
         .distinct()
         .collect(Collectors.toList());
 System.out.println(String.format("distinct列表数据:%s" , distinctStrs));
  • filter 过滤
代码语言:javascript复制
 // example : filter 过滤
 List<Integer> columns = Stream.of(1 ,2 ,-1,3,4,5,6,7,8,9)
         .filter(n -> n > 0)
         .collect(Collectors.toList());
 System.out.println(String.format("filter列表数据:%s" , columns));
  • map 映射
代码语言:javascript复制
// example : map 映射
List<String[]> mapArras = Stream.of("hello","hi")
         .map(e -> e.split("") )
         .distinct()
         .collect(Collectors.toList());
 // List<String[]>类型的,不能直接打印
 mapArras.forEach(System.out::println);
  • filterMap 映射汇总
代码语言:javascript复制
// example : flatMap 映射汇总
List<String> mapStrs = Stream.of("hello","hi")
         .map(e -> e.split(""))
         .flatMap(Arrays::stream)
         .distinct()
         .collect(Collectors.toList());
 // 通过.flatMap(Arrays::stream)转成string数据
 mapStrs.forEach(s->System.out.println(s));
  • limit 限制
代码语言:javascript复制
// example :limit限制
List<Integer> ints = IntStream.range(1,1000).limit(10)
         .boxed()
         .collect(Collectors.toList());
 // 打印[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 System.out.println(ints);
  • peek 观察者 peek会提供一个消费函数,每个元素被消费时都会执行这个钩子函数
代码语言:javascript复制
// example :peek  观察者
Arrays.stream(new String[]{"a","b","c","d","e"})
      // 每个元素被消费时都会执行这个钩子
      .peek(System.out::println)
      .count();
  • sorted 排序
代码语言:javascript复制
//example :sorted 排序
List<Integer> sortedInts = Stream.of(1 ,9, 3, 2, 10,5,8)
    .sorted(
            (a , b) -> {
                return a >b ? 1 :-1;
            }
    )
    //.sorted(Comparator.comparingInt(a -> a))
    .collect(Collectors.toList());
System.out.println(sortedInts);
  • skip 跳过 skip 跳过丢弃了前n个元素的流,如果流中的元素小于或者等于n,则返回空的流
代码语言:javascript复制
// example : skip 跳过
List<String> skipStrs = Stream.of("a", "b", "c", "d", "e","f","g","h","i")
         //丢弃了前n个元素的流,如果流中的元素小于或者等于n,则返回空的流
         .skip(2)
         .collect(Collectors.toList());
 System.out.println(skipStrs);

8、terminal operations

  • match 断言
代码语言:javascript复制
public boolean 	allMatch(Predicate<? super T> predicate)
public boolean 	anyMatch(Predicate<? super T> predicate)
public boolean 	noneMatch(Predicate<? super T> predicate)

  • allMatch只有在所有的元素都满足断言时才返回true,否则flase,流为空时总是返回true
  • anyMatch只有在任意一个元素满足断言时就返回true,否则flase,
  • noneMatch只有在所有的元素都不满足断言时才返回true,否则flase
  • count 计数
代码语言:javascript复制
 // example :count 计数
 String[] arr = new String[]{"a","b","c","d" , "e"};
 long count = Arrays.stream(arr).count();
 System.out.println(count);
  • collect 收集
代码语言:javascript复制
// example : collect 收集
List<String> strs = Stream.of("a", "b" , "c" , "d" , "e", "b")
        .collect(Collectors.toList());
System.out.println(strs);

方法

返回类型

作用

toList()

List<T>

把流中元素收集到List,List<T> result = list.stream().collect(Collectors.toList());

toSet()

Set<T>

把流中元素收集到Set,Set<T> result = list.stream().collect(Collectors.toSet());

toCollection()

Collection<T>

把流中元素收集到集合,Collection<T> result = lsit.stream().collect(Collectors.toCollection(ArrayListL::new));

counting()

Long

计算流中元素的个数,long count = lsit.stream().collect(Collectors.counting());

summingInt()

Integer

对流中元素的整数属性求和,int total = lsit.stream().collect(Collectors.counting());

averagingInt

Double

计算元素Integer属性的均值,double avg = lsit.stream().collect(Collectors.averagingInt(Student::getAge));

summarizingInt

IntSummary

Statistics收集元素Integer属性的统计值,IntSummaryStatistics result = list.stream().collect(Collectors.summarizingInt(Student::getAge));

joining

Stream

连接流中的每个字符串,String str = list.stream().map(Student::getName).collect(Collectors.joining());

maxBy

Optional<T>

根据比较器选择最大值,Opetional<Student> max = list.stream().collect(Collectors.maxBy(comparingInt(Student::getAge)))

minBy

Optional<T>

根据比较器选择最小值,Optional<Student> min= list.stream().collect(Collectors.minBy(comparingInt(Student::getAge)));

reducing

规约产生的类型

从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值,int total = list.stream().collect(Collectors.reducing(0, Student::getAge, Integer::sum));

collectingAndThen

转换函数返回的类型

包裹另一个收集器,对其结果转换,int how = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));

groupingBy

Map<K, List<T>>

根据某属性值对流分组,属性为K,结果为,Map<Integer, List<Student>> map = list.stream().collect(Collectors.groupingBy(Student::getStatus));

partitioningBy

Map<Boolean, List<T>>

根据true或false进行分区,Map<Boolean, List<Student>> map = list.stream().collect(Collectors.partitioningBy(Student::getPass));

  • find 返回

  • findAny()返回任意一个元素,如果流为空,返回空的Optional,对于并行流来说,它只需要返回任意一个元素即可
  • findFirst()返回第一个元素,如果流为空,返回空的Optional。
  • forEach、forEachOrdered 遍历 forEach遍历流的每一个元素,对于有序流按照它的encounter order顺序执行,你可以使用forEachOrdered方法
代码语言:javascript复制
Stream.of(1,2,3,4,5).forEach(System.out::println);
  • min、max

  • max返回流中的最大值,
  • min返回流中的最小值。
代码语言:javascript复制
 // example :max、min
long minV = Stream.of(1,2,3,4,5).max(
        new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        }
).get();
long maxV = Stream.of(1,2,3,4,5).max(
    new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }
).get();
System.out.println(String.format("min value :%s , max value :%s", minV ,maxV));
  • reduce 归约 reduce是一个变成操作,依照某种运算规则依次执行,所以reduce可以用于做sum,min,max,average等等操作

方法

描述

reduce(BinaryOperator b)

可以将流中元素反复结合起来,得到一个值,返回 Optional

reduce(T iden, BinaryOperator b)

可以将流中元素反复结合起来,得到一个值,返回 T

reduce(U identity, BiFunction a, BinaryOperator combiner)

可以将流中元素反复结合起来,得到一个值,返回 Optional

代码语言:javascript复制
 // example : reduce
 // reduce用于求和
 List<Integer> sumInts = Stream.of(1 ,2,3,4,5,6,7,8,9).collect(Collectors.toList());
 Optional<Integer> sum = sumInts.stream().reduce(Integer::sum);
 System.out.println(String.format("reduce计算的总值:%s" , sum));
  • toArray
代码语言:javascript复制
// example : toArray
Integer[] integers = Stream.of(1 ,2,3,4,5,6,7,8,9).toArray(Integer[]::new);
System.out.println(integers);
  • concat 组合
代码语言:javascript复制
 // example : concat 组合
 List<String> list1 = Stream.of("a","b","c").collect(Collectors.toList());;
 List<String> list2 = Stream.of("d","e","f").collect(Collectors.toList());;
 Stream.concat(list1.stream() , list2.stream()).forEach(System.out::println);

9、Stream灵活应用例子

  • 数据去重 数据去重,除了前面的distinct,也可以用TreeSet来做去重,总之,stream非常灵活,开发者可以根据自己理解,进行编程
代码语言:javascript复制
handleModels.stream().collect(
Collectors.collectingAndThen(
        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(HandleModel::getUserCode))), ArrayList::new)
);
  • 数据筛选
代码语言:javascript复制
 List<ControlVo> timeoutControlList = list.stream().filter(e-> e.getDays()!=null && e.getDays() < 0 ).sorted(Comparator.comparing(ControlVo::getDays)).collect(Collectors.toList());
  • PO、VO、DTO的转换
代码语言:javascript复制
// PO转DTO
dtoList = handleModels.stream().map(e -> {
     HandleDto dto = new HandleDto ();
     BeanUtil.copyProperties(e, dto);
     return dto;
 }).collect(Collectors.toList());
  • 和Optional一起使用 list有数据时候,才进行遍历
代码语言:javascript复制
Optional.ofNullable(list).ifPresent(
    hlist -> {
        hlist.stream().forEach((e) -> {
          
        });
});
  • 分挑存储 什么是分挑存储?就是根据集合实体类的一个字段,进行分组然后重新组合成新的集合

ps:按照StuffId进行分组

代码语言:javascript复制
 Map<String, List<AttachmentDto>> attachmentGroup = 
attachmentDtos.stream()
.filter(item -> StringUtils.isNotBlank(item.getStuffId()))
.collect(Collectors.groupingBy(AttachmentDto::getStuffId));

10、附录参考资料

  • https://www.runoob.com/java/java8-new-features.html
  • https://beginnersbook.com/2017/10/java-8-features-with-examples/
  • https://howtodoinjava.com/java-8-tutorial/

0 人点赞