Java提效神器Stream的一些冷门技巧汇总

目录
  • Stream
  • Filter
  • Foreach
  • Map
  • Sorted
  • Match
  • count
  • reduce
  • parallelStream
  • IntStream.range(a,b)
  • new Random().ints()
  • Supplier
  • Consumer
    • 1.    accept方法
    • 2.    andThen方法
  • ifPresent
  • Collect
    • 1. 函数
    • 2. Collector 接口
    • 3. 工具函数
      • 1. toList()
      • 2.joining()
      • 3.groupingBy()
      • 4.reducing()
  • 总结

Stream

使用这个方法创建一个 Stream 对象。

new ArrayList<>().stream()

Filter

过滤器,里面传递一个函数,这个函数的返回结果如果为 true 则保留这个元素,否则的话丢弃这个元素。

        stringCollection
                .stream()
                .filter((s) -> s.startsWith("a"))
                .forEach(System.out::println);

Foreach

遍历,消费。

        stringCollection
                .stream()
                .filter((s) -> s.startsWith("a"))
                .forEach(System.out::println);

Map

这个功能也是遍历,但是他是有返回值的,而上面的 Foreach 是没有返回值的,仅仅是单纯的消费。而且 Foreach 不能够链式调用,因为没有返回值,但是 Map 没问题。

        stringCollection
                .stream()
                .map(String::toUpperCase)
                .sorted(Comparator.reverseOrder())
                .forEach(System.out::println);

Sorted

这个方法是用来排序的,里面传递的函数就是一个比较器,也可以不传递参数,使用默认的就好。

        stringCollection
                .stream()
                .sorted(( x, y)-> y.length()-x.length())
                .filter((s) -> s.startsWith("a"))
                .forEach(System.out::println);

Match

根据在给定的 stream 对象中是否含有指定内容返回 true 或者 false 。

具体的有:

  • allMatch
  • anyMatch
  • noneMatch
        boolean anyStartsWithA = stringCollection
                .stream()
                .anyMatch((s) -> s.startsWith("a"));

        boolean allStartsWithA = stringCollection
                .stream()
                .allMatch((s) -> s.startsWith("a"));

        boolean noneStartsWithZ = stringCollection
                .stream()
                .noneMatch((s) -> s.startsWith("z"));

count

计算集合中的元素的个数。

long startsWithB = stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();

reduce

这个函数就是类似于斐波那契数列,每次传递的参数是上一次的结果和从集合中取出的新元素。第一次默认取出了第一个元素和第二个元素。

简单的例子就是,第一次取出 0,1 第二次取出 第一次reduce的结果作为第一个参数,取出 2 作为第二个参数,以此类推。

Optional<String> reduced =
        stringCollection
                .stream()
                .sorted()
                .reduce((s1, s2) -> s1 + "#" + s2);

parallelStream

并行的 steam 流,可以进行并行处理,这样会效率更高。在使用stream.foreach时这个遍历没有线程安全问题,但是使用parallelStream就会有线程安全问题,所有在parallelStream里面使用的外部变量,比如集合一定要使用线程安全集合,不然就会引发多线程安全问题。如果说需要保证安全性需要使用 reduce 和 collect,不过这个用起来超级麻烦!!!

long count = values.parallelStream().sorted().count();

IntStream.range(a,b)

可以直接生成 从 a 到 b 的整数这里还是遵循编程语言的大多数约定,那就是含头不含尾。

IntStream.range(0, 10)
    .forEach(System.out::println);

输出的结果是

0
1
2
3
4
5
6
7
8
9

new Random().ints()

获取一系列的随机值,这个接口出来的数据是连续不断的,所以需要用limit来限制一下。

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

Supplier

Supplier<String> stringSupplier=String::new;
stringSupplier.get();

该接口就一个抽象方法get方法,不用传入任何参数,直接返回一个泛型T的实例.就如同无参构造一样

Consumer

1.    accept方法

​        该函数式接口的唯一的抽象方法,接收一个参数,没有返回值.

2.    andThen方法

在执行完调用者方法后再执行传入参数的方法.

public class ConsumerTest {
    public static void main(String[] args) {
        Consumer<Integer> consumer = (x) -> {
            int num = x * 2;
            System.out.println(num);
        };
        Consumer<Integer> consumer1 = (x) -> {
            int num = x * 3;
            System.out.println(num);
        };
        consumer.andThen(consumer1).accept(10);
    }

先执行了 consumer.accept(10) 然后执行了 consumer1.accept(10)

ifPresent

针对一个optional 如果有值的话就执行否则不执行。

IntStream
    .builder()
    .add(1)
    .add(3)
    .add(5)
    .add(7)
    .add(11)
    .build()
    .average()
    .ifPresent(System.out::println);

average 执行结果就是一个 optional

Collect

他有两种调用方式

  <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);

 <R, A> R collect(Collector<? super T, A, R> collector);

下面主要介绍一下这两种方式的使用方法:

1. 函数

第一种调用方式的接口如下

  <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
  • supplier 这个参数就是提供一个容器,可以看到最后 collect 操作的结果是一个 R 类型变量,而 supplier 接口最后需要返回的也是一个 R 类型的变量,所以说这里返回的是收集元素的容器。
  • accumulator 参数,看到这个函数的定义是传入一个 R 容器,后面则是 T 类型的元素,需要将这个 T 放到 R 容器中,即这一步是用来将元素添加到容器中的操作。
  • conbiner 这个参数是两个容器,即当出现多个容器的时候容器如何进行聚合。

一个简单的例子:

String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,StringBuilder::append).toString();
//等价于上面,这样看起来应该更加清晰
String concat = stringStream.collect(() -> new StringBuilder(),(l, x) -> l.append(x), (r1, r2) -> r1.append(r2)).toString();

2. Collector 接口

第二种方案是更高级的用法采用了 Collector 接口:

 <R, A> R collect(Collector<? super T, A, R> collector);

可以看到他返回的还是一个 R 类型的变量,也就是容器。

Collector接口是使得collect操作强大的终极武器,对于绝大部分操作可以分解为旗下主要步骤,提供初始容器->加入元素到容器->并发下多容器聚合->对聚合后结果进行操作

static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
        private final Supplier<A> supplier;
        private final BiConsumer<A, T> accumulator;
        private final BinaryOperator<A> combiner;
        private final Function<A, R> finisher;
        private final Set<Characteristics> characteristics;

        CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Function<A,R> finisher,
                      Set<Characteristics> characteristics) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.combiner = combiner;
            this.finisher = finisher;
            this.characteristics = characteristics;
        }

        CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Set<Characteristics> characteristics) {
            this(supplier, accumulator, combiner, castingIdentity(), characteristics);
        }

        @Override
        public BiConsumer<A, T> accumulator() {
            return accumulator;
        }

        @Override
        public Supplier<A> supplier() {
            return supplier;
        }

        @Override
        public BinaryOperator<A> combiner() {
            return combiner;
        }

        @Override
        public Function<A, R> finisher() {
            return finisher;
        }

        @Override
        public Set<Characteristics> characteristics() {
            return characteristics;
        }
    }

可以看到我们可以直接 new CollectorImpl 然后将这些函数传入,另外还有一种简单的方式就是 使用 Collector.of()依然可以直接传入函数。和 new CollectorImpl 是等价的。

3. 工具函数

1. toList()

容器: ArrayList::new

加入容器操作: List::add

多容器合并: left.addAll(right); return left;

   public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
    }

2.joining()

容器: StringBuilder::new

加入容器操作: StringBuilder::append

多容器合并: r1.append(r2); return r1;

聚合后的结果操作: StringBuilder::toString

    public static Collector<CharSequence, ?, String> joining() {
        return new CollectorImpl<CharSequence, StringBuilder, String>(
                StringBuilder::new, StringBuilder::append,
                (r1, r2) -> { r1.append(r2); return r1; },
                StringBuilder::toString, CH_NOID);
    }

3.groupingBy()

roupingBy是toMap的一种高级方式,弥补了toMap对值无法提供多元化的收集操作,比如对于返回Map<T,List<E>>这样的形式toMap就不是那么顺手,那么groupingBy的重点就是对Key和Value值的处理封装.分析如下代码,其中classifier是对key值的处理,mapFactory则是指定Map的容器具体类型,downstream为对Value的收集操作.

   public static <T, K, D, A, M extends Map<K, D>>
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream) {
       .......
    }

一个简单的例子

//原生形式
   Lists.<Person>newArrayList().stream()
        .collect(() -> new HashMap<Integer,List<Person>>(),
            (h, x) -> {
              List<Person> value = h.getOrDefault(x.getType(), Lists.newArrayList());
              value.add(x);
              h.put(x.getType(), value);
            },
            HashMap::putAll
        );
//groupBy形式
Lists.<Person>newArrayList().stream()
        .collect(Collectors.groupingBy(Person::getType, HashMap::new, Collectors.toList()));
//因为对值有了操作,因此我可以更加灵活的对值进行转换
Lists.<Person>newArrayList().stream()
        .collect(Collectors.groupingBy(Person::getType, HashMap::new, Collectors.mapping(Person::getName,Collectors.toSet())));
// 还有一种比较简单的使用方式 只需要传递一个参数按照key来划分
Map<Integer, List<Person>> personsByAge = persons
            .stream()
    .collect(Collectors.groupingBy(p -> p.age));

4.reducing()

reducing是针对单个值的收集,其返回结果不是集合家族的类型,而是单一的实体类T

容器: boxSupplier(identity),这里包裹用的是一个长度为1的Object[]数组,至于原因自然是不可变类型的锅

加入容器操作: a[0] = op.apply(a[0], t)

多容器合并: a[0] = op.apply(a[0], b[0]); return a;

聚合后的结果操作: 结果自然是Object[0]所包裹的数据a -> a[0]

优化操作状态字段: CH_NOID

  public static <T> Collector<T, ?, T>
    reducing(T identity, BinaryOperator<T> op) {
        return new CollectorImpl<>(
                boxSupplier(identity),
                (a, t) -> { a[0] = op.apply(a[0], t); },
                (a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
                a -> a[0],
                CH_NOID);
    }

简单来说这个地方做的事情和 reduce 是一样的,第一个 id 传入的就是 reduce 的初始值,只是他把它包装成一个 长度为1的数组了。

//原生操作
final Integer[] integers = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream()
        .collect(() -> new Integer[]{0}, (a, x) -> a[0] += x, (a1, a2) -> a1[0] += a2[0]);
//reducing操作
final Integer collect = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream()
        .collect(Collectors.reducing(0, Integer::sum));
//当然Stream也提供了reduce操作
final Integer collect = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream().reduce(0, Integer::sum)

总结

到此这篇关于Java提效神器Stream的一些冷门技巧的文章就介绍到这了,更多相关Java Stream技巧内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java8 Stream API 详细使用方法与操作技巧指南

    本文实例讲述了Java8 Stream API 详细使用方法与操作技巧.分享给大家供大家参考,具体如下: 1. 概述 Java 8 引入的一个重要的特性无疑是 Stream API.Stream 翻译过来是"流",突然想到的是大数据处理有个流式计算的概念,数据通过管道经过一个个处理器(Handler)进行筛选,聚合,而且流都具有向量性,强调的是对数据的计算处理,而集合强调的是数据集.Stream可以看做是一个可操作的数据集序列,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找.

  • 5分钟让你快速掌握java8 stream常用开发技巧

    前言 如果有些朋友以前没有使用过java8 stream这种链式编程方式做开发,想学习一下. 如果有些朋友只学习了一部分用法,想学习更多. 如果有些朋友想看看有没有好的示例适用于实际工作当中. 那么恭喜你,这篇文章非常适合你. 首先,我们一起看看stream的继承关系: Stream.IntStream.LongStream.DoubleStream的父接口都是BaseStream.BaseStream的四个子接口方法都差不多,只是IntStream.LongStream.DoubleStrea

  • Java 8 Stream 的终极技巧——Collectors 功能与操作方法详解

    本文实例讲述了Java 8 Stream 的终极技巧--Collectors 功能与操作方法.分享给大家供大家参考,具体如下: 1. 前言 昨天在 Collection移除元素操作 相关的文章中提到了 Collectors .相信很多同学对这个比较感兴趣,那我们今天就来研究一下 Collectors . 2. Collectors 的作用 Collectors 是 Java 8 加入的操作类,位于 java.util.stream 包下.它会根据不同的策略将元素收集归纳起来,比如最简单常用的是将

  • Java提效神器Stream的一些冷门技巧汇总

    目录 Stream Filter Foreach Map Sorted Match count reduce parallelStream IntStream.range(a,b) new Random().ints() Supplier Consumer 1.    accept方法 2.    andThen方法 ifPresent Collect 1. 函数 2. Collector 接口 3. 工具函数 1. toList() 2.joining() 3.groupingBy() 4.r

  • Java 8系列之Stream中万能的reduce用法说明

    reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型.比如,之前提到count.min和max方法,因为常用而被纳入标准库中.事实上,这些方法都是reduce操作. reduce方法有三个override的方法: Optional<T> reduce(BinaryOperator<T> accumulator); T reduce(T identity, BinaryOperator<T> accumulator); <

  • java jdk1.8 使用stream流进行list 分组归类操作

    我就废话不多说了,大家还是直接看代码吧~ import com.alibaba.fastjson.JSON; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @author czw */ public class Foo{ private String name; private String type; private Double typeValue; p

  • 详解Java中的reactive stream协议

    背景 每个数据流都有一个生产者一个消费者.生产者负责产生数据,而消费者负责消费数据.如果是同步系统,生产一个消费一个没什么问题.但是如果在异步系统中,就会产生问题. 因为生产者无法感知消费者的状态,不知道消费者到底是繁忙状态还是空闲状态,是否有能力去消费更多的数据. 一般来说数据队列的长度都是有限的,即使没有做限制,但是系统的内存也是有限的.当太多的数据没有被消费的话,会导致内存溢出或者数据得不到即使处理的问题. 这时候就需要back-pressure了. 如果消息接收方消息处理不过来,则可以通

  • Java效率提升神器jOOR

    目录 前言 jOOR特点 常用API测试 测试APIS 代理功能 jOOR实现方式 动态编译 结论 前言 Java中的原生反射库虽然方法不多,但写起来却非常繁琐, 比如: public static <T> T create(HttpRequest httpRequest) { Object httpRequestEntity = null; try { Class<T> httpRequestEntityCls = (Class<T>) Class.forName(H

  • Java 中如何使用 stream 流

    目录 前言 一.筛选和切片 1.1.筛选 filter 1.2.去重 distinct 1.3.切片 limit 1.4.跳过元素 skip 1.5.排序 sorted 1.6.小结与综合应用 二.映射 map 三.查找和匹配 3.1.匹配 anyMatch.allMatch和noneMatch 方法 3.2.查找 findAny 与 findFirst 3.3.小结 四.归约 4.1.元素求和 reduce 后记 前言 如果你了解过 Liunx ,了解过 Liunx 的中管道命令 | ,那么你

  • Java 8中 Stream小知识小技巧方法梳理

    目录 前言 只能遍历的一次 Stream 那么为什么流只能遍历一次呢? 流操作 中间操作 终端操作 前言 上篇只是简单的动手操作操作了流(stream),那 stream 到底是什么呢? 官方的简短定义:“从支持数据处理操作的源生成的元素序列” 分成三部分: 元素序列:你可以简单将它类比于一样,不过集合说的是数据的集合,而 stream 重点在于表达计算.如我们之前说到的 filter.map.sorted.limit等等 源:昨天我提到,如果了解过 Liunx 管道命令的朋友们,会知道,Liu

  • Go并发与锁的两种方式该如何提效详解

    目录 并发不安全的例子 互斥锁 读写锁 小结 总结 并发安全,就是多个并发体在同一段时间内访问同一个共享数据,共享数据能被正确处理. 很多语言的并发编程很容易在同时修改某个变量的时候,因为操作不是原子的,而出现错误计算,比如一个加法运算使用中的变量被修改,而导致计算结果出错,典型的像统计商品库存. 个人建议只要涉及到共享变量统统使用channel,因为channel源码中使用了互斥锁,它是并发安全的. 我们可以不用,但不可以不了解,手中有粮心中不慌. 并发不安全的例子 数组是并发不安全的,在例子

  • Java分析Lambda表达式Stream流合并分组内对象数据合并

    目录 前言 需求 代码实现 依赖引入 设计实体类 测试代码 前言 之前写过<Lambda使用——JDK8新特性>,现在有一个分组合并的需求正好拿来小试牛刀. 需求 数据出自许多接口数据,需要将几个接口数据根据省份id进行分组合并.举例说明: A接口返回List里面有值的的字段为:provinceId.field1.field2.field3 B接口返回List里面有值的的字段为:provinceId.field4.field5.field6 C接口返回List里面有值的的字段为:provinc

  • 详解Java对象转换神器MapStruct库的使用

    目录 前言 MapStruct简介 MapStruct入门 1. 引入依赖 2. 需要转换的对象 3. 创建转换器 4. 验证 5. 自动生成的实现类 MapStruct进阶 场景1:属性名称不同.(基本)类型不同 场景2:统一映射不同类型 场景3:固定值.忽略某个属性.时间转字符串格式 场景4:为某个属性指定转换方法 场景5:多个参数合并为一个对象 场景6:已有目标对象,将源对象属性覆盖到目标对象 场景7:源对象两个属性合并为一个属性 小结 前言 在我们日常开发的程序中,为了各层之间解耦,一般

随机推荐