StreamAPI

Stream API

什么是 Stream 流

Stream将要处理的元素集合看作一种流 ,流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算

特性:

  • 无存储,为函数式编程而生。stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果
  • 对于stream 的修改都不会修改背后的数据源,比如对stream 执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream
  • 惰性执行。stream 上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行;只有调用终端操作时,中间操作才会执行
  • 可消费性。 stream 只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器,想要再次遍历必须重新生成

stream 流的生成方式

通过集合生成流

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream();

List<Test> testList = new LinkedList<>();
    testList.add(new Test("1", "邓闻", 18698151330L, "120104299103024011"));
    testList.add(new Test("2", "小明", 19842020131L, "1020420112191402511"));
    testList.add(new Test("3", "小强", 19842190221L, "10231450112191402511"));

Stream<Test> testStream = testList.stream();

通过数组生成流

  • 通过 Arrays.stream()方法生成流,并且该方法生成的流是数值流 IntStream 而不是 Stream<Integer>
int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);
  • 使用数值流可以避免计算过程中拆箱装箱,提高性能
  • Stream API 提供了 mapToInt,MapToDouble,mapTOLong 三种方式将对象流 stream 转换成对应的数值流,同时提供了boxed 方法将数值流转化为对象流
// 获取long 类型 电话号的值
LongStream result = testList.stream().map(Test::getTelphone).mapToLong(Long::longValue);
long[] telphones = result.toArray();
System.out.println(Arrays.toString(telphones));

// 字符串string 转int类型,获取id 的值
int[] ids = testList.stream().map(Test::getId).mapToInt(Integer::valueOf).toArray();
System.out.println(Arrays.toString(ids));

通过值生成流

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

可以通过empty 方法生成一个空流

Stream<Integer> stream = Stream.empty();

通过文件生成流

通过Files.lines 方法得到一个流,并且得到的每个流是给定文件中的一行

Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())

通过函数生成

Stream API 提供了iterator 和 generate 两个静态方法从函数中生成流

  • iterator 方法接收两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator 生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);

  • generator 方法接收一个参数,方法参数类型为Supplier,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断
Stream<Double> stream = Stream.generate(Math::random).limit(5);

流的操作类型

  • 中间操作: 一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的,仅仅调用到这类方法,并没有真正的开始流的遍历,真正的遍历需等到终端操作时。
    • 无状态: 指元素的处理不受之前元素的影响
    • 有状态: 指该操作只有拿到所有元素之后才能继续下去
  • 终端操作: 一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。终端操作的执行,才会真正开始流的遍历
    • 非短路操作: 指必须处理所有元素才能得到最终结果
    • 短路操作: 指遇到某些符合条件的元素就可以得到结果 ,如A||B ,只要A 为true,则无需判断B的结果
stream 操作类型分类
中间操作 (Intermediate operations)无状态 unordered(); filter(); map(); mapToInt(); mapToLong(); mapToDouble(); flatMap(); flatMapToLong(); flatMapToDouble(); flatMapToInt(); peek();
有状态 distinct(); sorted(); limit(); skip();
终端操作 (Terminal operations)非短路操作 forEach();forEachOrdered(); toArray();reduce();collect(); max();min();count();
短路操作anyMatch();allMatch();noneMatch();findFirst();findAny()

所有中间操作

方法说明
sequential返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象
parallel返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象
unordered返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象
onClose返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象
close关闭Stream对象
filter元素过滤:对Stream对象按指定的Predicate进行过滤,返回的Stream对象中仅包含未被过滤的元素
map元素一对一转换:使用传入的Function对象对Stream中的所有元素进行处理,返回的Stream对象中的元素为原元素处理后的结果
mapToInt元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象
flatMap元素一对多转换:对原Stream中的所有元素进行操作,每个元素会有一个或者多个结果,然后将返回的所有元素组合成一个统一的Stream并返回;
distinct去重:返回一个去重后的Stream对象
sorted排序:返回排序后的Stream对象
peek使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象
limit获取有限个元素组成新的Stream对象返回
skip抛弃前指定个元素后使用剩下的元素组成新的Stream返回
takeWhile如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream。
dropWhile与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream。

所有终端操作

方法说明
iterator返回Stream中所有对象的迭代器;
spliterator返回对所有对象进行的spliterator对象
forEach对所有元素进行迭代处理,无返回值
forEachOrdered按Stream的Encounter所决定的序列进行迭代处理,无返回值
toArray返回所有元素的数组
reduce使用一个初始化的值,与Stream中的元素一一做传入的二合运算后返回最终的值。每与一个元素做运算后的结果,再与下一个元素做运算。它不保证会按序列执行整个过程。
collect根据传入参数做相关汇聚计算
min返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty
max与Min相反
count所有元素个数
anyMatch只要其中有一个元素满足传入的Predicate时返回True,否则返回False
allMatch所有元素均满足传入的Predicate时返回True,否则False
noneMatch所有元素均不满足传入的Predicate时返回True,否则False
findFirst返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回。
findAny返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl。
isParallel判断是否当前Stream对象是并行的

串行/并行流

parallelStream()

返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象

  • parallelStream() 支持并行执行,提高程序运行效率。但是线程不安全,如果使用不当可能会发生线程安全问题
  • parallelStream() 每次执行的结果都不相同,与多线程程序中执行的结果类似。如果希望最后顺序是按照原来Stream 的数据顺序,那可以调用forEachOrdered()方法
  • parallelStream() 使用的线程池是 ForkJoinPool.common ,可以通过设置 -Djava.util.concurrent.ForkJoinPool.common.parallelism = N 来调整线程池的大小
  • parallelStream() 默认的线程数量是计算机处理器的数量
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); 
numbers.parallelStream().forEach(System.out::println);
numbers.parallelStream().forEachOrdered(System.out::println);

unordered()

在并行流中 使用 unordered()方法,使流变得无序,提高并行效率

Arrays.asList("1", "2", "3", "4", "5")
    .parallelStream()
    .unordered()
    .limit(2).forEach(System.out::print);

串行流转并行流

除了直接创建并行流的方式,还可以通过parallel()把顺序流转换成并行流:

Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();

并行流转串行流

使用 sequential() 方法返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象

Arrays.asList("1", "2", "3").parallelStream().sequential();

filter() 筛选

stream 使用 filter() 进行筛选

通过使用filter() 进行条件筛选,filter 的方法参数为一个条件

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);

distinct() 去重

stream 使用 distinct() 快速去除重复的元素

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();

limit() 返回指定个数

stream 使用 limit() 方法指定返回流的个数,limit 的参数值必须 >= 0,否则将会抛出异常

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().limit(3);

skip () 跳过流中的元素

stream 使用 skip() 方法跳过流中的元素,skip 的参数值必须 >= 0,否则将会抛出异常

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().skip(2);

通过skip() 和 limit() 方法 的结合可实现类似于分页的操作

 // 第一页,20个对象
        Integer pageNum = 1;
        Integer pageSize = 20;
        testList.stream().skip((pageNum - 1) * pageSize ).limit(pageSize);

对象转换

map() 流映射

所谓流映射就是将接收的元素映射成另外一个元素
把数组流中的每一个值,使用所提供的函数执行一遍,得到元素个数相同的数组流

// 从数据集中 获取电话号码列表
List<Long> telphones =  testList.stream().map(Test::getTelphone).collect(Collectors.toList());

// 通过map() 方法转为另一个List 对象输出
String[] testArray = ids.split(",");
List<TestMark> list = Arrays.stream(testArray)
            .map(id -> {
            TestMark testMark = new TestMark();
            testMark.setAlarmId(Integer.valueOf(id));
            return testMark;
            }).collect(Collectors.toList());

flatMap() 流转换

stream 通过 flatMap() ,对流进行扁平化处理,将一个流中的每个值都转换为另一个流
flatMap 操作具有对该流的元素应用一对多变换的效果,然后将所得到的元素展平到新的流中

扁平化的理解: flat 是扁平的意思,它把数组流中的每一个值,使用所提供的函数执行一遍,一一对应,得到元素相同的数组流。只不过里面的元素也可能是一个子数组流。flatMap的扁平化意味着把这些子树组合并到一个数组以后,形成一个一级结构的数组,这个数组的元素个数大概率会和原数组流的个数不同。

flatMap 的应用一般是先map 再flatMap,先将每个元素做处理,然后将处理结果flat 平铺合并,返回一个完整的一级数据结构。

String[] words = new String[]{"Hello","World"};
        List<String> a = Arrays.stream(words)
                .map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(toList());
        a.forEach(System.out::print);

flatMap 和 map 的区别

map 是对每一个元素进行处理,flatmap 是对每一个元素进行处理后,如果这个元素的大小是 1 ,那么就和map操作一样;如果处理后是个List或者Array类型,那么flatmap 就会将这个List或者Array的每个元素变成一个新元素,放到集合里

元素匹配

allMatch() 匹配所有

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().allMatch(i -> i > 3)) {
    System.out.println("值都大于3");
}

anyMatch() 匹配其中一个

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
    System.out.println("存在大于3的值");
}

noneMatch() 全部不匹配

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
    System.out.println("值都小于3");
}

数学计算

统计

  • 通过 count() 方法统计
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();
  • 通过counting
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().collect(counting());

最大值/最小值获取

  • 通过 min() / max() 方法获取最大值,最小值
Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo);

OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min();
OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();
  • 通过minBy /maxBy 方法获取最大值,最小值
Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo));
Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo));
  • 通过reduce 方法获取最大值,最小值
Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max);

求和

  • 通过 sum() 方法求和
IntStream intStream = IntStream.of(50, 100, 150, 200, 250, 300);
int sumVal = intStream.sum();

testList.stream().mapToLong(Test::getTelphone).sum();
  • 通过 Collectors.summing 方法求和
    • summingLong 方法
    • summingInt 方法
    • summingDouble 方法

testList.stream().collect(Collectors.summingLong(Test::getTelphone));
  • 通过 reduce() 方法求和
testList.stream().mapToLong(Test::getTelphone).reduce(0,Long::sum);

reduce() 方法将流中的元素组合起来

Java 1.8 新特性——Stream 流中 Reduce 操作

查找

findFirst() 查找第一个

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();

findAny() 随机查找一个

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();

通过findAny 方法找到其中一个大于三的元素并打印,因为内部进行优化的原因,当找到第一个满足大于三的元素时就结束,该方法结果和findFirst 方法结果一样。提供findAny 方法是为了更好的利用并行流,FindFirst方法在并行上限制更多

sorted() 排序

正序排序

stream 使用 sorted() 进行排序

list.stream()
    .sorted(Comparator.comparing(list::getSignInTime)

倒序排序

使用 reversed()函数实现倒叙排序

list.stream()
    .sorted(Comparator.comparing(list::getSignInTime).reversed() 

包含Null 值处理的排序

stream 的 sorted 排序字段是不能为null 的,否则会抛出 NullPointException 错误

  • null 值对象放前面: 可使用 Comparator.nullsFirst(String::compareTo)
list.stream()
    .sorted(Comparator.comparing(list::getSignInTime,Comparator.nullsFirst(String::compareTo)))  
  • null 值对象放后面: 可使用 Comparator.nullsLast(Integer::compareTo)
list.stream()
    .sorted(Comparator.comparing(list::getSignInTime,Comparator.nullsLast(String::compareTo)))  

多个字段排序

使用 thenComparing() 方法实现多个字段的排序

list.stream()
.sorted(Comparator.comparing(list::getSignInTime)
.thenComparing(list::getId));

collect() 收集数据

  • Collectors.toList(); 转换成List 集合
  • Collectors.toCollection(ArrayList::new) 收集流中的数据(ArrayList)到指定的集合中
  • Collectors.toSet(); 转换成set集合
  • Collectors.toCollection(TreeSet::new); 转换成特定的set集合
  • Collectors.toMap(x->x ,x-> x + 1) 转换成map
  • Collectors.minBy(Integer::compare) 求最小值
  • Collectors.maxBy(Integer::compare) 求最大值
  • Collectors.summingInt(x -> x) 求和
  • Collectors.averagingInt() 平均值
  • Collectors.counting() 总个数
  • Collectors.summarizingDouble(x -> x ) 可以获取最大值,最小值,平均值,总和值,总数
 DoubleSummaryStatistics s = Stream.of(1,3,4).collect(Collectors.summarizingDouble(x->x));

    s.getAverage();
    s.getCount();
    s.getMax();
    s.getSum();
    s.getMin();
  • Collectors.GroupingBy(x -> x) 分组操作
Map<Integer,List<Integer>> map = Stream.of(1,3,3,4).collect(Collectors.groupingBy(x ->x));

Map<Object, Long> maps = Stream.of(1,3,3,4).collect(Collectors.groupingBy(x ->x, Collectors.counting()));

HashMap<Integer,Long> hashMap = Stream.of(1,3,3,4).collect(Collectors.groupingBy(x ->x,HashMap::new,Collectors.counting()));
  • 多字段分组
 testList.stream().collect(Collectors.groupingBy(Test::getId,Collectors.groupingBy(Test::getTelphone,Collectors.groupingBy(Test::getIdNumber))));
  • Collectors.partitioningBy(x->x > 2) 分区操作
    • 把数据分成两部分 key为 true/false,一个true 列表,一个false 列表
// 满足条件得放在true 列表, 不满足条件的放在false 列表
 Map<Boolean,List<Integer>> collect5 = Stream.of(1,3,4).collect(Collectors.partitioningBy(x -> x  > 2));

Map<Boolean,Long> collect4 = Stream.of(1,3,4).collect(Collectors.partitioningBy(x-> x > 2,Collectors.counting()));
  • Collectors.joining() 拼接字符串
    • joining() 无参数–等价于 joining(“”)
    • joining(CharSequence delimiter) 一个参数
    • joining(CharSequence delimiter, CharSequence prefix,CharSequence suffix) 三个参数(前缀+后缀)
Stream.of("a","b","c").collect(Collectors.joining("."));

Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 52, 95),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 55),
                new Student("柳岩", 52, 33)
        );
        
        //拼接操作
        // 无参:join()
        String joinStr1 = studentStream.map(s -> s.getName()).collect(Collectors.joining());
        System.out.println(joinStr1);
        // 一个参数:joining(CharSequence delimiter)
        String joinStr2 = studentStream.map(s -> s.getName()).collect(Collectors.joining(","));
        System.out.println(joinStr2);
        // 三个参数:joining(CharSequence delimiter, CharSequence prefix,CharSequence suffix)
        String joinStr3 = studentStream.map(s -> s.getName()).collect(Collectors.joining("—","^_^",">_<"));
        System.out.println(joinStr3);
  • Collectors.reducing(0,x -> x + 1,(x,y) -> x + y) 在累计求值得时候,还可以对参数值进行改变,这里是都+1再求和。

  • Collectors.collcetingAndThen(Collectors.joining(“.”),x-> x + “d”); 先执行collect操作后再执行第二个参数的表达式。这里是先拼接字符串,再在最后拼接"d"。

  • Collectors.mapping(…) 跟map操作类似,只是参数有点区别

Stream.of("a","b","c").collect(Collectors.mapping(x->x.toUpperCase(),Collectors.joining(".")));

参考文献

Java8的Stream()与ParallelStream()的区别说明

巧用 Java 8 的 Stream 来优化代码,太简洁了

map 和 flatmap 的区别
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值