Java8新特性Stream的完全使用指南

什么是Stream

Stream是Java 1.8版本开始提供的一个接口,主要提供对数据集合使用流的方式进行操作,流中的元素不可变且只会被消费一次,所有方法都设计成支持链式调用。使用Stream API可以极大生产力,写出高效率、干净、简洁的代码。

如何获得Stream实例

Stream提供了静态构建方法,可以基于不同的参数创建返回Stream实例

使用Collection的子类实例调用stream()或者parallelStream()方法也可以得到Stream实例,两个方法的区别在于后续执行Stream其他方法的时候是单线程还是多线程

Stream<String> stringStream = Stream.of("1", "2", "3");
//无限长的偶数流
Stream<Integer> evenNumStream = Stream.iterate(0, n -> n + 2);

List<String> strList = new ArrayList<>();
strList.add("1");
strList.add("2");
strList.add("3");
Stream<String> strStream = strList.stream();
Stream<String> strParallelStream = strList.parallelStream();

filter

filter方法用于根据指定的条件做过滤,返回符合条件的流

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//获得只包含正数的流,positiveNumStream -> (1,2,3)
Stream<Integer> positiveNumStream = numStream.filter(num -> num > 0);

map

map方法用于将流中的每个元素执行指定的转换逻辑,返回其他类型元素的流

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//转换成字符串流
Stream<String> strStream = numStream.map(String::valueOf);

mapToInt mapToLong mapToDouble

这三个方法是对map方法的封装,返回的是官方为各个类型单独定义的Stream,该Stream还提供了适合各自类型的其他操作方法

Stream<String> stringStream = Stream.of("-2", "-1", "0", "1", "2", "3");
IntStream intStream = stringStream.mapToInt(Integer::parseInt);
LongStream longStream = stringStream.mapToLong(Long::parseLong);
DoubleStream doubleStream = stringStream.mapToDouble(Double::parseDouble);

flatMap

flatMap方法用于将流中的每个元素转换成其他类型元素的流,比如,当前有一个订单(Order)列表,每个订单又包含多个商品(itemList),如果要得到所有订单的所有商品汇总,就可以使用该方法,如下:

Stream<Item> allItemStream = orderList.stream().flatMap(order -> order.itemList.stream());

flatMapToInt flatMapToLong flatMapToDouble

这三个方法是对flatMap方法的封装,返回的是官方为各个类型单独定义的Stream,使用方法同上

distinct

distinct方法用于对流中的元素去重,判断元素是否重复使用的是equals方法

Stream<Integer> numStream = Stream.of(-2, -1, 0, 0, 1, 2, 2, 3);
//不重复的数字流,uniqueNumStream -> (-2, -1, 0, 1, 2, 3)
Stream<Integer> uniqueNumStream = numStream.distinct();

sorted

sorted有一个无参和一个有参的方法,用于对流中的元素进行排序。无参方法要求流中的元素必须实现Comparable接口,不然会报java.lang.ClassCastException异常

Stream<Integer> unorderedStream = Stream.of(5, 6, 32, 7, 27, 4);
//按从小到大排序完成的流,orderedStream -> (4, 5, 6, 7, 27, 32)
Stream<Integer> orderedStream = unorderedStream.sorted();

有参方法sorted(Comparator<? super T> comparator)不需要元素实现Comparable接口,通过指定的元素比较器对流内的元素进行排序

Stream<String> unorderedStream = Stream.of("1234", "123", "12", "12345", "123456", "1");
//按字符串长度从小到大排序完成的流,orderedStream -> ("1", "12", "123", "1234", "12345", "123456")
Stream<String> orderedStream = unorderedStream.sorted(Comparator.comparingInt(String::length));

peek

peek方法可以不调整元素顺序和数量的情况下消费每一个元素,然后产生新的流,按文档上的说明,主要是用于对流执行的中间过程做debug的时候使用,因为Stream使用的时候一般都是链式调用的,所以可能会执行多次流操作,如果想看每个元素在多次流操作中间的流转情况,就可以使用这个方法实现

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

输出:
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR

limit(long maxSize)

limit方法会对流进行顺序截取,从第1个元素开始,保留最多maxSize个元素

Stream<String> stringStream = Stream.of("-2", "-1", "0", "1", "2", "3");
//截取前3个元素,subStringStream -> ("-2", "-1", "0")
Stream<String> subStringStream = stringStream.limit(3);

skip(long n)

skip方法用于跳过前n个元素,如果流中的元素数量不足n,则返回一个空的流

Stream<String> stringStream = Stream.of("-2", "-1", "0", "1", "2", "3");
//跳过前3个元素,subStringStream -> ("1", "2", "3")
Stream<String> subStringStream = stringStream.skip(3);

forEach

forEach方法的作用跟普通的for循环类似,不过这个可以支持多线程遍历,但是不保证遍历的顺序

Stream<String> stringStream = Stream.of("-2", "-1", "0", "1", "2", "3");
//单线程遍历输出元素
stringStream.forEach(System.out::println);
//多线程遍历输出元素
stringStream.parallel().forEach(System.out::println);

forEachOrdered

forEachOrdered方法可以保证顺序遍历,比如这个流是从外部传进来的,然后在这之前调用过parallel方法开启了多线程执行,就可以使用这个方法保证单线程顺序遍历

Stream<String> stringStream = Stream.of("-2", "-1", "0", "1", "2", "3");
//顺序遍历输出元素
stringStream.forEachOrdered(System.out::println);
//多线程遍历输出元素,下面这行跟上面的执行结果是一样的
//stringStream.parallel().forEachOrdered(System.out::println);

toArray

toArray有一个无参和一个有参的方法,无参方法用于把流中的元素转换成Object数组

Stream<String> stringStream = Stream.of("-2", "-1", "0", "1", "2", "3");
Object[] objArray = stringStream.toArray();

有参方法toArray(IntFunction<A[]> generator)支持把流中的元素转换成指定类型的元素数组

Stream<String> stringStream = Stream.of("-2", "-1", "0", "1", "2", "3");
String[] strArray = stringStream.toArray(String[]::new);

reduce

reduce有三个重载方法,作用是对流内元素做累进操作

第一个reduce(BinaryOperator<T> accumulator)

accumulator 为累进操作的具体计算

单线程等下如下代码

boolean foundAny = false;
T result = null;
for (T element : this stream) {
 if (!foundAny) {
 foundAny = true;
 result = element;
 }
 else
 result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();
Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//查找最小值
Optional<Integer> min = numStream.reduce(BinaryOperator.minBy(Integer::compareTo));
//输出 -2
System.out.println(min.get());

//过滤出大于5的元素流
numStream = Stream.of(-2, -1, 0, 1, 2, 3).filter(num -> num > 5);
//查找最小值
min = numStream.reduce(BinaryOperator.minBy(Integer::compareTo));
//输出 Optional.empty
System.out.println(min);

第二个reduce(T identity, BinaryOperator<T> accumulator)

identity 为累进操作的初始值
accumulator 同上

单线程等价如下代码

T result = identity;
for (T element : this stream)
 result = accumulator.apply(result, element)
return result;
Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//累加计算所有元素的和,sum=3
int sum = numStream.reduce(0, Integer::sum);

第三个reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)

identity和accumulator同上

combiner用于多线程执行的情况下合并最终结果

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
int sum = numStream.parallel().reduce(0, (a, b) -> {
 System.out.println("accumulator执行:" + a + " + " + b);
 return a + b;
}, (a, b) -> {
 System.out.println("combiner执行:" + a + " + " + b);
 return a + b;
});
System.out.println("最终结果:"+sum);

输出:
accumulator执行:0 + -1
accumulator执行:0 + 1
accumulator执行:0 + 0
accumulator执行:0 + 2
accumulator执行:0 + -2
accumulator执行:0 + 3
combiner执行:2 + 3
combiner执行:-1 + 0
combiner执行:1 + 5
combiner执行:-2 + -1
combiner执行:-3 + 6
最终结果:3

collect

collect有两个重载方法,主要作用是把流中的元素作为集合转换成其他Collection的子类,其内部实现类似于前面的累进操作

第一个collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)

supplier 需要返回开始执行时的默认结果

accumulator 用于累进计算用

combiner 用于多线程合并结果

单线程执行等价于如下代码

R result = supplier.get();
for (T element : this stream)
 accumulator.accept(result, element);
return result;

第二个collect(Collector<? super T, A, R> collector)

collector其实是对上面的方法参数的一个封装,内部执行逻辑是一样的,只不过JDK提供了一些默认的Collector实现

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
List<Integer> numList = numStream.collect(Collectors.toList());
Set<Integer> numSet = numStream.collect(Collectors.toSet());

min

min方法用于计算流内元素的最小值

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
Optional<Integer> min = numStream.min(Integer::compareTo);

max

min方法用于计算流内元素的最大值

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
Optional<Integer> max = numStream.max(Integer::compareTo);

count

count方法用于统计流内元素的总个数

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//count=6
long count = numStream.count();

anyMatch

anyMatch方法用于匹配校验流内元素是否有符合指定条件的元素

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//判断是否包含正数,hasPositiveNum=true
boolean hasPositiveNum = numStream.anyMatch(num -> num > 0);

allMatch

allMatch方法用于匹配校验流内元素是否所有元素都符合指定条件

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//判断是否全部是正数,allNumPositive=false
boolean allNumPositive = numStream.allMatch(num -> num > 0);

noneMatch

noneMatch方法用于匹配校验流内元素是否都不符合指定条件

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//判断是否没有小于0的元素,noNegativeNum=false
boolean noNegativeNum = numStream.noneMatch(num -> num < 0);

findFirst

findFirst方法用于获取第一个元素,如果流是空的,则返回Optional.empty

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
//获取第一个元素,firstNum=-2
Optional<Integer> firstNum = numStream.findFirst();

findAny

findAny方法用于获取流中的任意一个元素,如果流是空的,则返回Optional.empty,因为可能会使用多线程,所以不保证每次返回的是同一个元素

Stream<Integer> numStream = Stream.of(-2, -1, 0, 1, 2, 3);
Optional<Integer> anyNum = numStream.findAny();

总结

到此这篇关于Java8新特性Stream的完全使用指南就介绍到这了,更多相关Java8 Stream使用指南内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java8中Lambda表达式使用和Stream API详解

    前言 Java8 的新特性:Lambda表达式.强大的 Stream API.全新时间日期 API.ConcurrentHashMap.MetaSpace.总得来说,Java8 的新特性使 Java 的运行速度更快.代码更少.便于并行.最大化减少空指针异常. 0x00. 前置数据 private List<People> peoples = null; @BeforeEach void before () { peoples = new ArrayList<>(); peoples

  • Java8中利用stream对map集合进行过滤的方法

    前言 Stream 是用函数式编程方式在集合类上进行复杂操作的工具,其集成了Java 8中的众多新特性之一的聚合操作,开发者可以更容易地使用Lambda表达式,并且更方便地实现对集合的查找.遍历.过滤以及常见计算等. 最近公司在大张旗鼓的进行代码审核,从中也发现自己写代码的不好习惯.一次无意的点到了公司封装的对map集合过滤的方法,发现了stream.于是研究了一下.并对原有的代码再次结合Optional进行重构下 原有方法说明 主要处理过滤条件Map对象,过滤掉了null和空字符串 等操作 这

  • Java8新特性Stream流实例详解

    什么是Stream流? Stream流是数据渠道,用于操作数据源(集合.数组等)所生成的元素序列. Stream的优点:声明性,可复合,可并行.这三个特性使得stream操作更简洁,更灵活,更高效. Stream的操作有两个特点:可以多个操作链接起来运行,内部迭代. Stream可分为并行流与串行流,Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换.串行流就不必再细说了,并行流主要是为了为了适应目前多核机器的时代,提高系统CP

  • 详解Java8新特性Stream之list转map及问题解决

    List集合转Map,用到的是Stream中Collectors的toMap方法:Collectors.toMap 具体用法实例如下: //声明一个List集合 List<Person> list = new ArrayList(); list.add(new Person("1001", "小A")); list.add(new Person("1002", "小B")); list.add(new Person

  • java8中parallelStream性能测试及结果分析

    测试1 @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 20, time = 3, timeUnit = TimeUnit.SECONDS) @Fork(1) @State(Scope.Benchmark) public cla

  • Java8中Stream使用的一个注意事项

    Stream简介 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel aggregate operations. 我们来解读一下上面的那句话: Stream是元素的集合,这点让Stream看起来用些类似Iterator: 可以支持顺序和并行的对原Stream进行汇聚的操作: 大家可以把Stream当成一个高级版本的Iterator.原始版本的Iterator,用户只能一个一个的遍历

  • 关于Java8 parallelStream并发安全的深入讲解

    背景 Java8的stream接口极大地减少了for循环写法的复杂性,stream提供了map/reduce/collect等一系列聚合接口,还支持并发操作:parallelStream. 在爬虫开发过程中,经常会遇到遍历一个很大的集合做重复的操作,这时候如果使用串行执行会相当耗时,因此一般会采用多线程来提速.Java8的paralleStream用fork/join框架提供了并发执行能力.但是如果使用不当,很容易陷入误区. Java8的paralleStream是线程安全的吗 一个简单的例子,

  • java8使用Stream API方法总结

    Stream是java8中处理集合的关键抽象概念,它可以指定您希望对集合进行的操作,可以执行非常复杂的查找.过滤和映射数据等操作.使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询. Stream 的三个操作步骤 1.创建Stream. 得到Stream流的第一种方式: 可以通过Collection系列集合提供提供的Stream()或parallelStream @Test public void test1() { //可以通过Collection系列集合提供提供的

  • 详解Java8 Collect收集Stream的方法

    Collection, Collections, collect, Collector, Collectos Collection是Java集合的祖先接口. Collections是java.util包下的一个工具类,内涵各种处理集合的静态方法. java.util.stream.Stream#collect(java.util.stream.Collector<? super T,A,R>)是Stream的一个函数,负责收集流. java.util.stream.Collector 是一个收

  • java8 stream 操作map根据key或者value排序的实现

    引言 最近小编自己一个人在负责一个项目的后台开发,其中有一部分是统计相关的功能,所以需要一些排序或者分组的操作,之前这种操作小编觉得还是比较麻烦的,虽热有一些现成的工具类,但是工具类的写法也是比较复杂的,但是如果使用java8 stream流的话就比较简单了,并且代码量会大大的减少,下面总结几个对map的操作. 1.map 根据value排序 Map<String,BigDecimal> map =new HashMap<>(); map.put("one",

随机推荐