公告: 博客现已通过!准备内容移植中。。。

Mirrors

原以素衣度韶华,恐将流年误!

lambda 随笔

@

目录

JDK1.8 Lambda & Stream

参考链接:

https://www.cnblogs.com/CarpenterLee/p/6637118.html#4486817

https://www.cnblogs.com/CarpenterLee/p/6675568.html

https://blog.csdn.net/xiliunian/article/details/88343762

https://blog.csdn.net/xiliunian/article/details/88364200

https://blog.csdn.net/xiliunian/article/details/88773718

数据源

下述所有操作皆基于此

List<Engineer> engineerList = Lists.newArrayList();
@Before
public void init(){
    engineerList.add(new Engineer().setId(1).setAge(12).setName("张三").setSalary(new BigDecimal("1000")));
    engineerList.add(new Engineer().setId(2).setAge(14).setName("李四").setSalary(new BigDecimal("2000")));
    engineerList.add(new Engineer().setId(3).setAge(20).setName("王五").setSalary(new BigDecimal("5000")));
    engineerList.add(new Engineer().setId(4).setAge(18).setName("赵六").setSalary(new BigDecimal("8000")));
    engineerList.add(new Engineer().setId(5).setAge(40).setName("陈七").setSalary(new BigDecimal("20000")));
    engineerList.add(new Engineer().setId(6).setAge(20).setName("钱八").setSalary(new BigDecimal("17000")));
}

引言

lambda

何为lambda,对于java来说万物皆对象。但是有些时候我们的参数可能只需要一个处理不需要做那么重的操作。故此我们有了lambda函数式编程。其标准格式为:

(参数...) -> {代码块}

左侧小括号内的参数无则留空例如() -> {1};一个则小括号可以直接省略例如req -> {req == null};有多个则逗号间隔(req1,req2,req3) -> {}

右侧大括号内邪恶参数若只有一个则括号可以省略例如req -> req == null;如果有返回对象且语句只有一条则可以省略return关键字例如req -> req.getName();如果有多条语句需要用;间隔有返回值则return不可省略例如req -> {String name = req.getName();\r\n return name+"-test";}

注意:

  1. lambda虽然为函数但是其内部得局部变量不能与外界得方法的变量同名

  2. lambda的return不会导致调用method的终止其只是终止了当前函数。

  3. lambda表达式允许用引用final变量、static变量和局部变量但是只允许修改静态变量,以及修改局部变量的属性而不可改变变量的引用指向。如果我们想使用局部变量需要保证此局部变量在lambda外没有使用。

    为什么会有如上这种限制呢?主要是因为局部变量的引用及基本类型的局部变量都保存在栈当中。如若lambda能直接修改栈上的变量,lambda 在另一条线程运行(不一定是新线程运行),那么lambda使用后,当前方法继续执行后其访问的该变量并非是原始变量而是副本。故此我们用开发工具写lambda的时候如果直接使用局部变量有时会报错提示需要复制一个变量出来使用,而使用静态变量则不需要因为静态变量是全局的存放在堆当中的可以直接修改值。

    由于对局部变量的限制,Lambda 表达式在 Java 中又称为闭包或匿名函数。它们可以作为参数传递给方法,并且可以访问其作用域之外的变量。但是它们不能修改定义 Lambda 的方法的局部变量的内容,这些变量必须是隐式最终的。因此可以认为 Lambda 是对值封闭,而不是对变量封闭,因为可以访问局部变量,但不可修改值。

Stream

何为Stream,数据流式操作,流一旦开启数据则数据不会再新增和删除,流是无法复用这意味着一个 Stream只能被使用一次。我们可以用不同的方法来对流的数据进行不同的操作如filter(过滤),map(提取),reduce(汇总)find(单个数据),match(计算),sort(排序)等.这让我们在一定程度上避免了遍历和if语句的编写,对集合的操作有了更高层次的抽象。
Stream的操作分为 Intermediate (中间)操作、Terminal (终端)及short-circuiting操作。上诉所说的filtermap皆是Intermediate。可以有多个中间操作存在,我们可以将多项操作串联起来表达出一个复杂的处理例如先过滤再提取接着汇总然后输出Termianl
Stream的操作可以单线程执行,也可以使用并行流parallelStream
Stream接口通常以函数式接口作为参数,因此其对lambda的支持非常好,Stream配合Lambda可以写出非常简练的链式代码。

注意:

  1. Stream不具有复用性,使用一次下次若再次使用会报错stream has already been operated upon or closed
  2. Strem中 如果没有Terminal(终端) 语句Intermediate(中间操作)将不会运行,没有终端去输出,过程将不会运行

构造方法:

list.stream();//通过list获得
Stream.of(T t...); // 通过Stream.of 获得与 Arrays.asList() 
Stream.if(T[]);//通过Stream.of 数组获得
List.parallelStream();//通过List获得并行流
Stream.of(T t...).parallel();//将Stream转为并行流

可以做到将一个Stream转为并行的多个stream流,其开辟线程数默认和CPU 核数一致。其默认通过JDK1.7的ForJoinPool来实现的多线程任务。
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12")可以做到修改其线程数,不推荐修改。


常量词解释

@FunctionalInterface 函数式接口

​ FunctionalInterface 首先它得是一个接口,然后就是在这个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。
​ 注意:虽然它只能有一个抽象方法,但是他允许定义默认方法和静态方法。例如下述的 [Consumer](#noun -consumer) 就是一个典型的函数式接口

Consumer(消费者)

Consumer 接口就是一个消费型的接口,通过传入参数,然后处理此参数。即可写作 (T t) -> {}。其有一个默认的方法andThen 会返回一个Consumer ,默认实现的是在 当前调用者的accept之后再调用传入的afteraccept方法。

Consumer相同的有DoubleConsumer、IntConsumer、LongConsumer 与之类似的有BiConsumerBiConsumer 为两个参数也叫(二元消费者)

@FunctionalInterface
public interface Consumer<T> {

    //抽象接口
    void accept(T t);
    
    //默认的方法 处理后置组成
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
	}
}

例1:

@Test
public void toConsumer(){
    //实现了Consumer接口
    Consumer<Engineer> engineerConsumer = new Consumer<Engineer>() {
        @Override
        public void accept(Engineer e) {
            e.setAge(e.getAge()+100);
        }
    };
    //将集合中的元素的age加上100
    testConsumer(engineerList,engineerConsumer);
    engineerList.forEach(System.out::println);
    System.out.println("==========lambda表达式==========");
    engineerList.forEach(t -> t.setAge(t.getAge()+100));
    engineerList.forEach(System.out::println);
}

/**
     * 将集合中的属性通过 Consumer 进行处理处理
     */
private static <T> void testConsumer(List<T> list,Consumer<T> consumer){
	for (T t : list) {
		consumer.accept(t);
	}
}

结果:

Engineer(id=1, age=112, name=张三, salary=1000)
Engineer(id=2, age=114, name=李四, salary=2000)
Engineer(id=3, age=120, name=王五, salary=5000)
Engineer(id=4, age=118, name=赵六, salary=8000)
Engineer(id=5, age=140, name=陈七, salary=20000)
Engineer(id=6, age=120, name=钱八, salary=17000)
==========lambda表达式==========
Engineer(id=1, age=212, name=张三, salary=1000)
Engineer(id=2, age=214, name=李四, salary=2000)
Engineer(id=3, age=220, name=王五, salary=5000)
Engineer(id=4, age=218, name=赵六, salary=8000)
Engineer(id=5, age=240, name=陈七, salary=20000)
Engineer(id=6, age=220, name=钱八, salary=17000)

例2: Consumer的andThen

@Test
public void toConsumer2() {
    //实现了Consumer接口
    Consumer<Engineer> engineerConsumer = new Consumer<Engineer>() {
        @Override
        public void accept(Engineer e) {
            e.setName(e.getName() + "-电商组");
        }
    };
    // 所有技工的名字加上(研发部)-电商组
    engineerList.forEach(engineerConsumer.andThen(t -> t.setName(t.getName() + "[研发二部]")));
    engineerList.forEach(System.out::println);
}

结果:

Engineer(id=1, age=12, name=张三-电商组[研发二部], salary=1000)
Engineer(id=2, age=14, name=李四-电商组[研发二部], salary=2000)
Engineer(id=3, age=20, name=王五-电商组[研发二部], salary=5000)
Engineer(id=4, age=18, name=赵六-电商组[研发二部], salary=8000)
Engineer(id=5, age=40, name=陈七-电商组[研发二部], salary=20000)
Engineer(id=6, age=20, name=钱八-电商组[研发二部], salary=17000)

Supplier(提供者)

​ supplier 是一个类似于参数构建的接口,仅有一个 get()方法会返回一个参数。其可以提供一个参数供其他方法调用。即可写作 () -> T;与之相同的接口还有IntSupplier 、DoubleSupplier 、LongSupplier 、BooleanSupplier;orEleseGet就是一个典型的Supplier

@FunctionalInterface
public interface Supplier<T> {
	//抽象接口
    T get();
}

例1:

Engineer engineer = null;
Supplier<Engineer> supplier = new Supplier<Engineer>() {
    @Override
    public Engineer get() {
        // 返回一个对象
        return new Engineer().setName("张三").setSalary(new BigDecimal(new Random().nextInt()));
    }
};
engineer = Optional.ofNullable(engineer).orElseGet(supplier);
System.out.println(engineer);

结果:

Engineer(age=null, name=张三, salary=-120016191)

Predicate (谓语;条件判断)

​ Predicate 接口是一个谓词型接口,其实,这个就是一个类似于 bool 类型的条件判断的接口。提供接口test传入一个参数返回一个boolean值。用lambda表达式可以写作(T t) -> return true|false.其内部自身还维护了条件运算符的一些功能及一个hashCode判断的静态方法。Stream中的filter 为一个Predicate接口。

@FunctionalInterface
public interface Predicate<T> {

   // 抽象的校验方法
    boolean test(T t);

    //条件与
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

		
	//条件否定
	default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    //条件或
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    //参数等比
	//不同于以上这个是一个静态方法用以对比对象hashCode
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

例1:

Predicate<Engineer> predicate = new Predicate<Engineer>() {
    @Override
    public boolean test(Engineer engineer) {
        return engineer.getName() != null && engineer.getName().equals("张三");
    }
};
engineerList.stream().filter(predicate).forEach(System.out::println);

结果:

Engineer(id=1, age=12, name=张三, salary=1000)

例2:条件与(and)

age > 14且sallary > 5000

Predicate<Engineer> predicate = new Predicate<Engineer>() {
    @Override
    public boolean test(Engineer engineer) {
        return engineer.getName() != null && engineer.getAge() > 14;
    }
};
//条件与
System.out.println("========= 条件与 =========");
Predicate<Engineer> andPredicate = predicate.and((t) -> 
           new BigDecimal("5000").compareTo(t.getSalary()) < 0);
engineerList.stream().filter(andPredicate).forEach(System.out::println);

结果:

========= 条件与 =========
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=6, age=20, name=钱八, salary=17000)

例3:条件或(or)

age > 14 或name 包含 陈

Predicate<Engineer> predicate = new Predicate<Engineer>() {
	@Override
	public boolean test(Engineer engineer) {
		return engineer.getName() != null && engineer.getAge() > 14;
	}
};
//条件或
System.out.println("========= 条件或 =========");
Predicate<Engineer> orPredicate = predicate.or(t -> t.getName().contains("陈"));
engineerList.stream().filter(orPredicate).forEach(System.out::println);

结果:

========= 条件或 =========
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=6, age=20, name=钱八, salary=17000)

例4:条件否定(negate)

!(age > 14)

Predicate<Engineer> predicate = new Predicate<Engineer>() {
	@Override
	public boolean test(Engineer engineer) {
		return engineer.getName() != null && engineer.getAge() > 14;
	}
};
//条件否定
System.out.println("========= 条件否定 =========");
engineerList.stream().filter(predicate.negate()).forEach(System.out::println);

结果:

========= 条件否定 =========
Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=2, age=14, name=李四, salary=2000)

例5:对象比对(Hash)

equals

System.out.println("========= 传参对比 Hash 比对=========");
Engineer targetCompareEngineer = 
    new Engineer().setId(6).setAge(20).setName("钱八").setSalary(new BigDecimal("17000"));
engineerList.stream().filter(Predicate.isEqual(targetCompareEngineer))
    .forEach(System.out::println);

结果:

========= 传参对比 Hash 比对=========
Engineer(id=6, age=20, name=钱八, salary=17000)

Function(中转器)-UnaryOperator-BinaryOperator

Function<T,R> 接口为一个功能型接口。提供接口apply传入一个参数R转换成参数T。顾名思义其就是一个函数。用lambda表达式可以写作r -> t。与Function相同的有IntFunction<R>(将Integer转为R)、IntToDoubleFunction(提供applyAsDoubleInteger转为Double)、LongFunction<R>UnaryOperator<T>(出入参一致)BiFunction<T,T,T>

Function

@FunctionalInterface
public interface Function<T, R> {

   //抽象的校验方法
    R apply(T t);

	//前置处理 接受一个方法返回一个参数并把返回参数带入到apply参与处理
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    //后置处理 接受一个参数返回一个参数将当前的apply的运算结果带入其中参与处理
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    
	//静态方法返回一个输入和输出一致的Function
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

UnaryOperator

继承于Function<T,R>不过它的出入参数是同样一个参数。也就是说,传入泛型T类型的参数,调用apply后,返回也T类型的参数;这个接口定义了一个静态方法,返回泛型对象的本身;

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
 
    /**
     * Returns a unary operator that always returns its input argument.
     *
     * @param <T> the type of the input and output of the operator
     * @return a unary operator that always returns its input argument
     */
    static <T> UnaryOperator<T> identity() {
        return t -> t;
    }
}

BinaryOperator

继承于BiFunction<T, U, R> 不过BinaryOperator<T>的输入参数是同样一个参数,也就是说,传入泛型T类型的参数,调用apply后,返回也T类型的参数。不过其内置了两个静态方法minBymaxBy

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
 	/**
     * 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
     * @param <T> 比较器的输入参数的类型
     * @param comparator 用来比较两个值的Comparator
     * @return 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
     * @throws 如果参数为NULL,就会抛出NullPointerException异常
     */
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    /**
     * 通过比较器Comparator来比较两个元素中较大的一个作为返回值返回。
     * @param <T> 比较器的输入参数的类型
     * @param comparator 用来比较两个值的Comparator
     * @return 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
     * @throws 如果参数为NULL,就会抛出NullPointerException异常
     */
    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}

例1:

Function<Engineer, String> engineerStringFunction = new Function<Engineer, String>() {
	@Override
	public String apply(Engineer e) {
		return e.getName();
	}
};
Stream<String> stringStream = engineerList.stream().map(engineerStringFunction);
stringStream.forEach(System.out::print);
System.out.println();
// 利用lambda表达式去写
System.out.println("===========利用lambda表达式去写==============");
engineerList.stream().map(Engineer::getName).forEach(System.out::print);

结果:

张三李四王五赵六陈七钱八
===========利用lambda表达式去写==============
张三李四王五赵六陈七钱八

例2:前置处理(compose)

  • e.setName(e.getName() + "-compose")
  • return name
Function<Engineer, String> engineerStringFunction = new Function<Engineer, String>() {
	@Override
	public String apply(Engineer e) {
		return e.getName();
	}
};
//处理前置组成  compose 前置处理
System.out.println("========= 前置处理 =========");
Stream<String> stringStream1 = engineerList.stream().map(
    engineerStringFunction.compose((e) -> e.setName(e.getName() + "-compose")));
stringStream1.forEach(System.out::println);

结果:

========= 前置处理 =========
张三-compose
李四-compose
王五-compose
赵六-compose
陈七-compose
钱八-compose

例3:后置处理(andThen)

  • return name
  • name + "-andThen"
Function<Engineer, String> engineerStringFunction = new Function<Engineer, String>() {
	@Override
	public String apply(Engineer e) {
		return e.getName();
	}
};
//处理后置组成
System.out.println("========= 后置处理 =========");
Stream<String> stringStream2 = engineerList.stream().map(
    engineerStringFunction.andThen((e) -> e + "-andThen"));
stringStream2.forEach(System.out::println);

结果:

========= 后置处理 =========
张三-andThen
李四-andThen
王五-andThen
赵六-andThen
陈七-andThen
钱八-andThen

例4:出入参一致(identity)

Function<Engineer, String> engineerStringFunction = new Function<Engineer, String>() {
	@Override
	public String apply(Engineer e) {
		return e.getName();
	}
};
//出入参一致
System.out.println("========= 出入参一致 =========");
Map<BigDecimal, Engineer> collect = engineerList.stream().collect(Collectors.toMap(Engineer::getSalary, Function.identity()));
Map<BigDecimal, String> collect1 = engineerList.stream().collect(Collectors.toMap(Engineer::getSalary, engineerStringFunction));
collect.forEach((k, v) -> System.out.println("使用 identity 时 =>[" + k + ":" + v + "]"));
collect1.forEach((k, v) -> System.out.println("使用 engineerStringFunction 时 =>[" + k + ":" + v + "]"));

结果:

========= 出入参一致 =========
使用 identity 时 =>[17000:Engineer(id=6, age=20, name=钱八, salary=17000)]
使用 identity 时 =>[2000:Engineer(id=2, age=14, name=李四, salary=2000)]
使用 identity 时 =>[8000:Engineer(id=4, age=18, name=赵六, salary=8000)]
使用 identity 时 =>[1000:Engineer(id=1, age=12, name=张三, salary=1000)]
使用 identity 时 =>[20000:Engineer(id=5, age=40, name=陈七, salary=20000)]
使用 identity 时 =>[5000:Engineer(id=3, age=20, name=王五, salary=5000)]
使用 engineerStringFunction 时 =>[17000:钱八]
使用 engineerStringFunction 时 =>[2000:李四]
使用 engineerStringFunction 时 =>[8000:赵六]
使用 engineerStringFunction 时 =>[1000:张三]
使用 engineerStringFunction 时 =>[20000:陈七]
使用 engineerStringFunction 时 =>[5000:王五]

例6: BinaryOperator和其minBy

BinaryOperator<String> stringBinaryOperator = (t1,t2) -> t1+t2;
System.out.println(stringBinaryOperator.apply("a", "b"));
/** minBy */
BinaryOperator<Engineer> engineerBinaryOperator = BinaryOperator.minBy((t1, t2) -> (t1.getSalary().compareTo(t2.getSalary())));
Engineer apply = engineerBinaryOperator.apply(
    new Engineer().setName("张三").setSalary(new BigDecimal("5.0")),
    new Engineer().setName("李四").setSalary(new BigDecimal("23.0")));
System.out.println(apply);

Collector接口

Collector接口用于Stream的collect方法的参数;其有三个泛型分别是:

T: 单个元素的类型

A:累计用的容器类型

R:输出的容器的类型

public interface Stream<T>{
     <R, A> R collect(Collector<? super T, A, R> collector);
}

其包含五个参数,正因为这五个参数所以我们呢有了很多可以操作的工具方法例如:toStettoList

  public static<T, A, R> Collector<T, A, R> of(
      // supplier参数用于生成结果容器,容器类型为A
      Supplier<A> supplier,
      //accumulator用于消费元素,这里的T就是元素,它会将流中的元素一个一个与结果容器A发生操作
      BiConsumer<A, T> accumulator,
      //combiner用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
      BinaryOperator<A> combiner,
      //finisher用于将结果转换将结果R 转为 结果A
      Function<A, R> finisher,
      //characteristics 表示当前Collector的特征值
      Characteristics... characteristics
  	){
      ...
  }

特征码Characteristics包括:CONCURRENTUNORDEREDIDENTITY_FINISH.

  • CONCURRENT: 结果只有一个,如果是并行流其将会触发combiner去进行结果合并;此特性意味着多条线程可以同时操作一个结果容器,因而结果容器必须为线程安全的。
  • UNORDERED:流中的元素无序,没屌用的特征码🐂
  • IDENTITY_FINISH: 表示中间操作所得类型与最终结果类型一致无须调用finisher进行转换。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jZ2AImKA-1615448693776)(C:\Users\雁鹏\AppData\Roaming\Typora\typora-user-images\image-20201030144339459.png)]

例1: toList() 源码解读

static final Set<Collector.Characteristics> CH_ID
            = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));

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

其首先创建了一个ArrayList集合(supplier),然后把每个元素通过List.add(T)给循环加入(accumulator),通过(left, right) -> { left.addAll(right); return left; }去归并并行流(combiner),CH_ID为特征码IDENTITY_FINISH

CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Set<Characteristics> characteristics) {
            this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}
private static <I, R> Function<I, R> castingIdentity() {
        return i -> (R) i;
}

castingIdentity为一个直接类型强转的方法i -> (R)i

例2: 自定义Collector

public class TestSetCollector implements Collector<Engineer, Set<Engineer>, Set<Engineer>> {

    private static AtomicInteger pointer = new AtomicInteger(0);

    //定义一个HashSet来归集元素
    @Override
    public Supplier<Set<Engineer>> supplier() {
        System.out.println(pointer.getAndIncrement() + "==>[supplier]调用");
        return new Supplier<Set<Engineer>>() {
            @Override
            public Set<Engineer> get() {
                return Sets.newHashSet();
            }
        };
    }

    
    @Override
    public BiConsumer<Set<Engineer>, Engineer> accumulator() {
        System.out.println(pointer.getAndIncrement() + "==>[accumulator]调用");
        return new BiConsumer<Set<Engineer>, Engineer>() {
            @Override
            public void accept(Set<Engineer> s, Engineer str) {
                s.add(str);
            }
        };
    }

    @Override
    public BinaryOperator<Set<Engineer>> combiner() {
        System.out.println(pointer.getAndIncrement() + "==>[combiner]调用");
        return new BinaryOperator<Set<Engineer>>() {
            @Override
            public Set<Engineer> apply(Set<Engineer> s1, Set<Engineer> s2) {
                s1.addAll(s2);
                return s1;
            }
        };
    }

    @Override
    public Function<Set<Engineer>, Set<Engineer>> finisher() {
        System.out.println(pointer.getAndIncrement() + "==>[finisher]调用");
        return new Function<Set<Engineer>, Set<Engineer>>() {
            @Override
            public Set<Engineer> apply(Set<Engineer> s1) {
                s1.add(new Engineer().setName("你被转换过了的"));
                return s1;
            }
        };
    }

    @Override
    public Set<Characteristics> characteristics() {
        System.out.println(pointer.getAndIncrement() + "==>[characteristics]调用");
        return Sets.newHashSet(Characteristics.IDENTITY_FINISH,
            Characteristics.CONCURRENT,
            Characteristics.UNORDERED);
    }
}

结果:

0==>[supplier]调用
1==>[accumulator]调用
2==>[combiner]调用
3==>[characteristics]调用
4==>[characteristics]调用
Engineer(id=8, age=24, name=李十, salary=5000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=7, age=30, name=孙九, salary=8000)
    ... 

执行语句

@Test
public void testCollect() {
   engineerList.stream().collect(new TestSetCollector()).forEach(System.out::println);
}

从打印结果看finisher()函数并没有被执行,原因在于我们在TestSetCollector.characteristics定义的特征码IDENTITY_FINISH.其在java.util.stream.ReferencePipeline#collect中有对其进行判断当特征码包含IDENTITY_FINISH的话则不需要进行finisher

public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
    A container;
    if (isParallel()
            && (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
            && (!isOrdered()||collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
        //supplier的执行
        container = collector.supplier().get();
        BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
        //accumulator的执行
        forEach(u -> accumulator.accept(container, u));
    }else {
        container = evaluate(ReduceOps.makeRef(collector));
    }
    //判断特征码是否包含IDENTITY_FINISH包含则直接强转,否则调用 finisher.apply
    return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
           ? (R) container
           : collector.finisher().apply(container);
}

Stream的一些操作

Intermediate (中间操作)

操作 含义
map (mapToInt, flatMap 等) 对象属性刷选
filter 条件过滤
distinct 去重
sorted;Comparator;sort 排序
peek 循环处理元素
limit 获取前n个元素
skip 删除前n个元素
parallel stream转为并行流
sequential stream转为串行流
unordered 将流无序化,
用于对顺序无要求的Stream
例如并行流其可以提升性能

Map;mapToInt;mapToDouble;..

其可以对Stream进行数据的刷选映射到另一个Strem对象上去(新对象为新创建),新的Stream对象的类型与提取值得类型有关。其内置了Int、Double和Long三个Map。

用法

//传入一个 function 函数
Stream stream = stream.map(Function<? super T, ? extends R> mapper);
//通过 lambda去提取值,如果对提取值有更多处理可用此方式
Stream stream1 = stream.mapToInt(engineers -> engineers.getAge()); 
//通过 Function 函数去获取参数值。
Stream stream2 = stream.mapToInt(Engineer::getAge);
例1:
Stream<Engineer> stream = engineers.parallelStream();
IntStream intStream = stream.mapToInt(engineers -> engineers.getAge());
intStream.forEach(e -> System.out.println(e));

结果:

the age is =>20
the age is =>18
the age is =>15
the age is =>14

filter 指定条件过滤

其可以对Stream中的对象进行条件过滤,输出过滤后的结果

用法

// 传入一个 Predicate 
Stream<T> stream = = stream.filter(Predicate<? super T> predicate);
Stream<T> stream = = stream.filter(Predicate<T>); //传入一个Predicate 函数 其会要求返回一个boolean值
Stream<T> stream = = stream.filter(engineer -> engineer.getAge() >= 18);//直接使用lambda进行
例1:
// ------- 获取age 大于等于18的 对象
Stream<Engineer> stream = engineers.stream(); //获得stream流
//过滤enginner取出age大于等于18的转为stream流
Stream<Engineer> engineerStream = stream.filter(engineer -> engineer.getAge() >= 18);
//循环输出
engineerStream.forEach(System.out::println);

结果:

Engineer{name='陈七', age=18, salary=2244}
Engineer{name='段誉', age=20, salary=7386}
Engineer{name='虚竹', age=21, salary=5648}
例2:
engineerList.stream().filter(e -> e.getAge() > 15).map(Engineer::getName).forEach(System.out::println);

distinct 根据hashCode去重

数据去重,与mysql的DISTINCT() 函数等意。其会根据hashCode 进行去重和Set类似

用法:

Stream<T> distinctStream = Stream.distinct();
例1:
//将integer去重
Stream<Integer> intStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 8, 9, 10).parallel();
Stream<Integer> distinct = intStream.distinct();
distinct.forEach(t -> System.out.println("result =>"+t));

运行结果:

result =>7
result =>6
result =>2
result =>3
result =>1
result =>8
result =>4
result =>9
result =>10
result =>5
例2: 去重的几种方式
//根据hashCode去重,可以直接使用 distinct
System.out.println("===========使用distinct===========");
engineerList.stream().distinct().collect(Collectors.toList()).forEach(System.out::println);
//根据hashCode去重,使用HashSet去重,
//区别于 distinct;distinct使用后stream任然处于 Intermediate(中间操作)的状态而HashSet处于Terminal(终端)状态
System.out.println("===========使用HashSet===========");
engineerList.stream().collect(Collectors.toSet()).forEach(System.out::println);
//根据 TreeSet 能传入 Comparator 来形成指定属性(name)的去重来把集合中的元素放入到TreeSet集合,然后把TreeSet集合转为List
//collectingAndThen解释: Collectors.collectingAndThen 将 集合通过 Collector 操作后再通过后续的Function 得到结果
//toCollection解释: 通过 Supplier构建一个参数将集合中的元素放入其中
System.out.println("===========使用Comparator来指定去重===========");
List<Engineer> collect = engineerList.stream().collect(
  Collectors.collectingAndThen(
     Collectors.toCollection(() -> 
          Sets.newTreeSet(Comparator.comparing(Engineer::getName))), Lists::newArrayList));
collect.forEach(System.out::println);
//使用filter来去重,使用外部属性来根据contain去重
System.out.println("===========使用 filter 来指定去重===========");
List<BigDecimal> containSalaryList = Lists.newArrayList();
engineerList.stream().filter(t -> {
     //判断集合是否有 salary 有则过滤 否则添加
     if (containSalaryList.contains(t.getSalary())) {
         return false;
     }
     containSalaryList.add(t.getSalary());
     return true;
}).forEach(System.out::println);

运行结果:

===========使用distinct===========
Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=6, age=20, name=钱八, salary=7000)
Engineer(id=7, age=30, name=孙九, salary=8000)
Engineer(id=8, age=24, name=李十, salary=5000)
===========使用HashSet===========
Engineer(id=8, age=24, name=李十, salary=5000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=7, age=30, name=孙九, salary=8000)
Engineer(id=6, age=20, name=钱八, salary=7000)
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=1, age=12, name=张三, salary=1000)
===========使用Comparator来指定去重===========
Engineer(id=7, age=30, name=孙九, salary=8000)
Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=8, age=24, name=李十, salary=5000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=6, age=20, name=钱八, salary=7000)
Engineer(id=5, age=40, name=陈七, salary=20000)
===========使用 filter 来指定去重===========
Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=6, age=20, name=钱八, salary=7000)

sorted;Comparator;sort;

数据排序,其会根据 java.util.Comparator 进行排序,也可以自行重写排序规则。Comparator 接口为一个函数式接口,可以使用lambda表达式。sortsorted 的区别在于 sort用于集合 list.sort(...)sorted 用于流stream.

用法:

//要求Comparator 返回一个Integer
list.sort(Comparator<T>);
Stream<Engineer> sorted = stream.sorted(Comparator<? super T> comparator);
objects.sort(Comparator.comparing(T::getXX()));//根据对象 T 的xx 属性来排序(升序)
objects.sort(Comparator.comparing(T::getXX()).reversed());//根据对象 T 的xx 属性来排序然后取反

注意: stored因涉及多参数的比较,故不要使用并行流 ParallelStream来进行操作。

Comparator 可以使用 reversed()来取反。

Comparator函数源码如下:

@FunctionalInterface
public interface Comparator<T> {
	int compare(T o1, T o2);
    
    //反转
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }
}

可以简写lambda表达式为 (t1,t2) -> return Integer. 负数为升序 正数为降序

例1: sort
ArrayList<Engineer> objects = Lists.newArrayList();
objects.add(new Engineer().setName("段誉").setAge(20).setSalary(4581L));
objects.add(new Engineer().setName("王五").setAge(14).setSalary(4481L));
objects.add(new Engineer().setName("陈六").setAge(30).setSalary(5581L));
objects.add(new Engineer().setName("虚竹").setAge(18).setSalary(9581L));
objects.sort(Comparator.comparing(Engineer::getAge));
System.out.println("age升序:"+objects);
 objects.sort(Comparator.comparing(Engineer::getAge).reversed());
System.out.println("age降序:"+objects);

结果:

age升序:[
AutoGenerate.Engineer(name=王五, age=14, salary=4481),
AutoGenerate.Engineer(name=虚竹, age=18, salary=9581), 
AutoGenerate.Engineer(name=段誉, age=20, salary=4581), 
AutoGenerate.Engineer(name=陈六, age=30, salary=5581)
]
age降序:[
AutoGenerate.Engineer(name=陈六, age=30, salary=5581), 
AutoGenerate.Engineer(name=段誉, age=20, salary=4581), 
AutoGenerate.Engineer(name=虚竹, age=18, salary=9581), 
AutoGenerate.Engineer(name=王五, age=14, salary=4481)
]
例2:storted

根据age 升序

Comparator<Engineer> comparator = new Comparator<Engineer>() {
    @Override
    public int compare(Engineer o1, Engineer o2) {
        return o1.getSalary().compareTo(o2.getSalary());
    }
};
engineerList.stream().sorted(comparator).forEach(System.out::println);
System.out.println("=========反转======");
engineerList.stream().sorted(comparator.reversed()).forEach(System.out::println);

结果:

Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=6, age=20, name=钱八, salary=17000)
Engineer(id=5, age=40, name=陈七, salary=20000)
=========反转======
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=6, age=20, name=钱八, salary=17000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=1, age=12, name=张三, salary=1000)
例3: lambda表达式 sorted
 engineerList.stream().sorted((t1, t2) -> t1.getAge() - t2.getAge()).forEach(System.out::println);
 //engineerList.stream().sorted(Comparator.comparingInt(Engineer::getAge)).forEach(System.out::println);//与上等价

结果:

Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=6, age=20, name=钱八, salary=17000)
Engineer(id=5, age=40, name=陈七, salary=20000)
例4: 根据指定字符排序

RuleBasedCollator 根据指定的<?<? 去排序字符。其会子匹配头表示。

 Comparator<UserDto> comparator = new Comparator<UserDto>() {
            @Override
            public int compare(UserDto o1, UserDto o2) {
                String myrule = "<陈<姜<罗<徐";
                RuleBasedCollator myrulecollato = null;
                try {
                    myrulecollato = new RuleBasedCollator(myrule);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                return myrulecollato.compare(o1.getAuthor(), o2.getAuthor());
            }
};

e.g.:

ArrayList<UserDto> userDtoList = new ArrayList<>();
userDtoList.add(new UserDto("让子弹飞","姜文",42));
userDtoList.add(new UserDto("一位陌生女人的来信","徐静蕾",35));//x
userDtoList.add(new UserDto("雾都孤儿","罗曼·波兰斯基",52));//l
userDtoList.add(new UserDto("芙蓉镇","姜文",36));//j
userDtoList.add(new UserDto("我和我的祖国","陈凯歌",46));
userDtoList.add(new UserDto("霸王别姬","陈凯歌",34));
userDtoList.add(new UserDto("金陵十三钗","张艺谋",34));
userDtoList.sort(comparator);
userDtoList.forEach(System.out::println);

结果:

UserDto{names='我和我的祖国', author='陈凯歌', age=46}
UserDto{names='霸王别姬', author='陈凯歌', age=34}
UserDto{names='让子弹飞', author='姜文', age=42}
UserDto{names='芙蓉镇', author='姜文', age=36}
UserDto{names='雾都孤儿', author='罗曼·波兰斯基', age=52}
UserDto{names='一位陌生女人的来信', author='徐静蕾', age=35}
UserDto{names='金陵十三钗', author='张艺谋', age=34}

peek 循环处理

循环处理元素与ForEach类似,区别是ForEach操作是属于Terminal操作,元素将会被消费,其后无法进行任何元素的逻辑。

用法

stream.peek(e -> {});
stream.peek(e -> peek(Consumer<T> action)); //接一个Consumer表达式
例1:
Stream<Engineer> stream = engineers.stream();
Stream<Engineer> peek =stream.sorted((e1, e2) -> (e1.getAge() - e2.getAge()))
    .peek(e -> {
        System.out.println("peek -->" + e);
        e.setAge(1000); //测试 peek 对象循环修改会真实修改对象
    }); //有返回值 Stream
Integer ageSum = peek.map(Engineer::getAge).reduce(Integer::sum).get(); //消费了 stream
System.out.println("ageSum -->"+ageSum);
try {
    stream.forEach(e -> System.out.println(e)); //此处会报错因为stream已被消费再次使用会出现错误
} catch (Exception e) {
    e.printStackTrace();
}
engineers.stream().forEach(e -> System.out.println("forEach -->"+e)); //无返回值

返回结果:

peek -->Engineer{name='张三', age=12, salary=4863}
peek -->Engineer{name='李四', age=14, salary=6538}
peek -->Engineer{name='王五', age=15, salary=7757}
peek -->Engineer{name='赵六', age=17, salary=406}
ageSum -->4000
java.lang.IllegalStateException: stream has already been operated upon or closed
.
. 缩减篇幅
.
ntellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
forEach -->Engineer{name='张三', age=1000, salary=4863}
forEach -->Engineer{name='李四', age=1000, salary=6538}
forEach -->Engineer{name='王五', age=1000, salary=7757}
forEach -->Engineer{name='赵六', age=1000, salary=406}

limit | skip 截取前n个|删除前n个

limt 获得前面指定个数元素,skip 删除前面指定个数元素

用法:

stream.limit(Num);//获得0-num个元素
stream.skip(Num);//删除0-num个元素

注意: limit或skip不可小于0,否则会抛出异常IllegalArgumentException

例1:
Stream<Engineer> engineerStream = engineers.stream();
engineerStream.sorted((e1,e2) -> e1.getAge() -e2.getAge())
        .forEach(e -> System.out.println(e));
System.out.println("*****************************");
engineerStream = engineers.stream();
engineerStream.sorted((e1,e2) -> e1.getAge() -e2.getAge())
        .limit(3).forEach(e -> System.out.println(e));//获得前面4个元素
System.out.println("*****************************");
engineerStream = engineers.stream();
engineerStream.sorted((e1,e2) -> e1.getAge() -e2.getAge())
        .skip(3).forEach(e -> System.out.println(e));//扔掉前面4个元素

结果:

Engineer{name='张三', age=12, salary=4013}
Engineer{name='李四', age=14, salary=1152}
Engineer{name='王五', age=15, salary=4836}
Engineer{name='赵六', age=17, salary=835}
*****************************
Engineer{name='张三', age=12, salary=4013}
Engineer{name='李四', age=14, salary=1152}
Engineer{name='王五', age=15, salary=4836}
*****************************
Engineer{name='赵六', age=17, salary=835}

Terminal (输出)

操作 含义
forEach 循环处理元素
forEachOrdered 有序循环处理元素
toArray 流转为数组
reduce 逻辑计算返回一个对象
collect 集合操作
min 获得最小
max 获得最大
count 获得条数
anyMatch 只要有一个满足则返回true
allMatch 全部满足则返回true
noneMatch 全部不满足则返回true
findFirst 获得流的第一个元素
findAny 获得流的任意一元素
iterator 返回流的迭代器

forEachOrdered 有序遍历

有序遍历,对并行流parallelStream 依然有效,解决了ForEach在并行流下排序会被打乱的问题

例1:
List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
list.stream().forEach(System.out::print);
System.out.println();
System.out.println("===========使用并行流===========");
list.parallelStream().forEach(System.out::print);
System.out.println();
System.out.println("===========使用forEachOrder让其排序===========");
list.parallelStream().forEachOrdered(System.out::print);

输出结果:

12345
===========使用并行流===========
35421
===========使用forEachOrder让其排序===========
12345

toArray 将Stream输出成数组

用法

Stream.toArray();
Stream.toArray(T::new);//T::new 等同于  new T()
例1:
Stream<Engineer> stream = engineers.stream();
Engineer[] engineers = stream.peek(engineer -> engineer.setAge(200)).toArray();
for (Engineer engineer : engineers) {
     System.out.println(engineer);
}

返回结果:

Engineer{name='张三', age=200, salary=7416}
Engineer{name='李四', age=200, salary=491}
Engineer{name='王五', age=200, salary=3507}
Engineer{name='赵六', age=200, salary=5509}

reduce

计算汇总,可以对流进行数据的计算逻辑从而得到一个结果。比如数量统计的count、最大\小max\min.

reduce有三个重载方法:

Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

Optional<T> reduce(BinaryOperator<T> accumulator)接受一个BinaryOperator<T>函数,(t1,t2) -> return t1 两个入参一个回参,且回参会带入到第二次运算的t1当中,首次的t1为角标为0的元素。注意其回参为Optional

T reduce(T identity, BinaryOperator<T> accumulator);,与第一种变形相同的是都会接受一个BinaryOperator接口,不同的是其会接受一个identity参数,用来指定循环的初始值。如果循环为空,就直接返回该值该方法不会返回Optional,因为该方法不会出现null。

<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);参数identityaccumulator与第二种的含义相同都是定义一个初始值以后参与accumulator的计算。区别在第三个参数combiner,第三个参数同样为BinaryOperator接口其入参为双元入参,入参类型为accumulator的返回值类型,它的作用在于并行流的结果汇总时把多个分支的数据进行计算。在串型的stream中其不会执行,只有在parallelStream中才会执行。原理如下:

sequential(串型)流中Fork/Join模式

/**
取出流中的头两个元素然后通过后述的lambda表达式,算出记为 新的t1,紧接着取出流中的第三个元素记为t2。最终将值返回,含义如下:
stream 			-> v1,v2,v3,v4,v...
第一步运行 reduce->  v1(t1),v2(t2) 算出等于vTemp
第二步运行 reduce->  vTemp(t1),v3(t2) 算出等于vTemp
第三步运算 reduce->  vTemp(t1),v4(t2) 算出等于vTemp
.
.
.
**/
Optional opt = seqStream.reduce((t1,t2) -> { return T});//返回值为opt,可以使用.orElseGet(() -> lambda)判断后获得结果

//---------------------------------------------------------------------------------
/**
取出startT 和流的第一个元素通过后述的lambda表达式,算出记为 新的t1,紧接着取出流中的第二个元素记为t2。最终将值返回,含义如下:
stream 			-> v1,v2,v3,v4,v...
第一步运行 reduce->  startT(t1),v1(t2) 算出等于vTemp
第二步运行 reduce->  vTemp(t1),v2(t2) 算出等于vTemp
第三步运算 reduce->  vTemp(t1),v3(t2) 算出等于vTemp
**/
T t = seqStream.reduce(startT,(t1,t2) -> {return T}); //返回值为 T

parallel(并型)流中

/**
取出流中的两个元素然后通过后述的lambda表达式,算出记为结果r1,同时另一条线程获得流中的另外两个元素通过计算得r2,最后将所有得结果进行递归合并得r1+r2+...(注意:此处不可单纯认为是r1+r2..,其类似于JDK1.7得Fork/join 模式将任务分给多个线程去运行然后各个线程内部合并运算后最终外部再进行合并运算),含义如下
原有元素: 		 ->v1,v2,v3,v4,v...
stream--Thread1 	-> v1,v2,...
stream--Thread2 	-> v3,v5,...
stream--Thread3 	-> v4,v5,...
... more 

第一步运行 Thread1 - reduce ->  v1(t1),v2(t2) 算出等于r1
	同时  Thread2 - reduce ->  v3(t1),v5(t2) 算出等于r2
	...more
第二步运行 Thread1 - reduce ->  v18(t1),r1(t2) 算出等于r1 (此处得v18为假设)
	同时  Thread2 - reduce ->  v9(t1),r2(t2)  算出等于r2 (此处得v5为假设)
	...more
.
.
.
第n步运算  reduce->  r1(t1),r2(t2) 算出等于 result
**/
Optional opt = parStream.reduce((t1,t2) -> { return T});//返回值为opt,可以使用.orElseGet(() -> lambda)判断后获得结果


//---------------------------------------------------------------------------------
/**
取出流中的1个元素然后与startT通过后述的lambda表达式,算出记为结果r1,同时另一条线程获得流中的另外1个元素与startT通过计算得r2,后多条线程又去流中各自获取一个元素与startT进行运算得出结果r3,r4,r5...,最后将所有得结果进行递归合并得r1+r2+...(注意:此处不可单纯认为是r1+r2..,其类似于JDK1.7得Fork/join 模式将任务分给多个线程去运行然后各个线程内部合并运算后最终外部再进行合并运算),含义如下
原有元素: 		 ->v1,v2,v3,v4,v...
stream--Thread1 	-> v1,v2,...
stream--Thread2 	-> v3,v5,...
stream--Thread3 	-> v4,v5,...
... more 

第一步运行 Thread1 - reduce ->  startT(t1),v2(t2) 算出等于threadResult1[0] = r1
	同时  Thread2 - reduce ->  startT(t1),v3(t2) 算出等于threadResult2[0] = r2
	...more
第二步运行 Thread1 - reduce ->  startT(t1),v18(t2) 算出等于threadResult1[1] = r18
	同时  Thread2 - reduce ->  startT(t1),v5(t2)  算出等于threadResult2[1] = r5
	...more
.
.
.
第n步运算  reduce->  算出等于threadResult1[0]+hreadResult1[1]+...+hreadResult2[0]+hreadResult2[1]... 算出等于 result
**/
T t = parStream.reduce(startT,(t1,t2) -> {return T});//返回值为 T

区别: 并行流时,起始数将会被计算 stream的长度次。而在串行时其只会被计算一次。由上述过程也可得知,在处理逻辑复杂并涉及到其他元素的结果时请慎重考虑使用并行计算。

基本类型可使用的函数列表

函数 含义
Number::min 最小
Number::max 最大
Number::sum 求和
String::concat 字符串拼接
例1:单个参数

从并行流的返回结果可以看出它是把1、2给了线程1,3、4给了线程2去执行的。

List<Integer> integers = Lists.newArrayList(1, 2, 3, 4);
AtomicInteger atomicInteger = new AtomicInteger(0);
BinaryOperator<Integer> operator = new BinaryOperator<Integer>() {
	@Override
    public Integer apply(Integer t1, Integer t2) {
        System.out.println("*******第:" + atomicInteger.addAndGet(1) + "次调用*******");
        System.out.println("t1=>" + t1);
        System.out.println("t2=>" + t2);
        t1 = t1 + t2;
    	return t1;
	}
};
System.out.println("[sequential]==>"+integers.stream().reduce(operator).get());
atomicInteger.set(0);
System.out.println("[parallel]==>"+integers.parallelStream().reduce(operator).get());

返回结果:

*******第:1次调用*******
t1=>1
t2=>2
*******第:2次调用*******
t1=>3
t2=>3
*******第:3次调用*******
t1=>6
t2=>4
[sequential]==>10
*******第:1次调用*******
t1=>3
t2=>4
*******第:2次调用*******
t1=>1
t2=>2
*******第:3次调用*******
t1=>3
t2=>7
[parallel]==>10
例2:有初始值

给了串行和并行流一样的起始值5,但是发现两个函数的值不一样一个是15一个是30.

分析查看并行流的执行过程发现其有四个使用了起始值的表达式分别是:5+3,5+4,5+25+1。而串行的只有一个使用了起始值5+1;从中可以看出并行流如果有起始值其结果将与预想的结果有差异.

List<Integer> integers = Lists.newArrayList(1, 2, 3, 4);
AtomicInteger atomicInteger = new AtomicInteger(0);
BinaryOperator<Integer> operator = new BinaryOperator<Integer>() {
    @Override
    public Integer apply(Integer t1, Integer t2) {
        System.out.println("*******第:" + atomicInteger.addAndGet(1) + "次调用*******");
        System.out.println("t1=>" + t1);
        System.out.println("t2=>" + t2);
        t1 = t1 + t2;
        return t1;
    }
};
System.out.println("[sequential]==>"+integers.stream().reduce(5,operator));
atomicInteger.set(0);
System.out.println("[parallel]==>"+integers.parallelStream().reduce(5,operator));

返回结果:

*******第:1次调用*******
t1=>5
t2=>1
*******第:2次调用*******
t1=>6
t2=>2
*******第:3次调用*******
t1=>8
t2=>3
*******第:4次调用*******
t1=>11
t2=>4
[sequential]==>15
*******第:1次调用*******
t1=>5
t2=>3
*******第:2次调用*******
t1=>5
t2=>4
*******第:3次调用*******
t1=>5
t2=>2
*******第:4次调用*******
t1=>5
t2=>1
*******第:6次调用*******
t1=>6
t2=>9
*******第:5次调用*******
t1=>8
t2=>7
*******第:7次调用*******
t1=>13
t2=>17
[parallel]==>30
例3:并发归并

使用并行流运行以后自行制定对多条线程的合并操作流程。

List<Integer> integers = Lists.newArrayList(1, 2, 3, 4);
AtomicInteger atomicInteger = new AtomicInteger(0);
List<Integer> result = integers.parallelStream().reduce(
    Lists.newArrayList(100),
    new BiFunction<List<Integer>, Integer, List<Integer>>() {
        @Override
        public List<Integer> apply(List<Integer> list, Integer item) {
            System.out.println("*******第:" + atomicInteger.addAndGet(1) + "次调用[accumulator]*******");
            System.out.println("t1=>" + list);
            System.out.println("t2=>" + item);
            List<Integer> integerList = Lists.newArrayList();
            integerList.addAll(list);
            integerList.add(item);
            return integerList;
        }
    },
    new BinaryOperator<List<Integer>>() {
        @Override
        public List<Integer> apply(List<Integer> node1, List<Integer> node2) {
            System.out.println("combiner");
            System.out.println("node1=>" + node1);
            System.out.println("node2=> " + node2);
            List<Integer> integerList = Lists.newArrayList();
            integerList.addAll(node1);
            integerList.addAll(node2);
            return integerList;
        }
    });
System.out.println(result);

返回结果:

*******第:1次调用[accumulator]*******
t1=>[100]
t2=>3
*******第:2次调用[accumulator]*******
t1=>[100]
t2=>4
combiner
node1=>[100, 3]
node2=> [100, 4]
*******第:3次调用[accumulator]*******
t1=>[100]
t2=>2
*******第:4次调用[accumulator]*******
t1=>[100]
t2=>1
combiner
node1=>[100, 1]
node2=> [100, 2]
combiner
node1=>[100, 1, 100, 2]
node2=> [100, 3, 100, 4]
[100, 1, 100, 2, 100, 3, 100, 4]
例4:内置函数:
List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println("默认最小值==>"+integers.stream().parallel().reduce(Integer::min).get());
System.out.println("带参判断最小值==>"+integers.stream().parallel().reduce(0,Integer::min));
System.out.println("默认最大值==>"+integers.stream().parallel().reduce(Integer::max).get());
System.out.println("带参判断最大值==>"+integers.stream().parallel().reduce(Integer.MAX_VALUE,Integer::max));
System.out.println("求和计算==>"+integers.stream().parallel().reduce(Integer::sum));

返回结果:

默认最小值==>1
带参判断最小值==>0
默认最大值==>9
带参判断最大值==>2147483647
求和计算==>Optional[45]v

Collects

Collectors是java.util.stream下的一个工具类,其内部提供了多个返回值为Collector的方法,我们可以直接将其拿来使用。

Collector是java.util.stream下的一个接口

用法

stream.collect(Collectors c);//内部可以接 java.util.stream.Collectors 中的方法

已知方法列表:

函数 含义
指定key转为Map
数据汇总
操作后接着怎么做

toList stream转List集合

例子:10633

 List<Engineer> collectList = enginnerList.stream().collect(Collectors.toList());
 System.out.println("流转[List]集合(toList)==>"+ JSON.toJSONString(collectList));

结果:

流转[List]集合(toList)==>[{"age":50,"name":"张三","salary":50000.00},
{"age":22,"name":"李四","salary":4900.00},
{"age":43,"name":"王五","salary":8000.00},
{"age":18,"name":"赵六","salary":6000.00},
{"age":36,"name":"陈七","salary":14000.00}]

toSet stream转Set集合

例子:

System.out.println("流转[Set]集合(toSet)==>"+ JSON.toJSONString(enginnerList.stream().collect(Collectors.toSet())));
System.out.println("=== 去重转Set ===");
//此处的set去重用到了Comparator.comparing() 方法
TreeSet<Engineer> collect = enginnerList.stream().collect(Collectors.toCollection(() -> Sets.newTreeSet(Comparator.comparing(Engineer::getAge))));
System.out.println("流转[Set]集合去重(toCollection)==>"+ JSON.toJSONString(collect));

输出:

流转[Set]集合(toSet)==>[
{"age":43,"name":"王五","salary":8000.00},
{"age":50,"name":"张三","salary":50000.00},
{"age":22,"name":"陈七","salary":14000.00},
{"age":22,"name":"李四","salary":4900.00},
{"age":18,"name":"赵六","salary":6000.00}]
=== 去重转Set ===
流转[Set]集合去重(toCollection)==>[
{"age":18,"name":"赵六","salary":6000.00},
{"age":22,"name":"李四","salary":4900.00},
{"age":43,"name":"王五","salary":8000.00},
{"age":50,"name":"张三","salary":50000.00}]

toMap stream转map集合

​ 将 stream转成map集合。提供三个函数如下:

toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
        BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
        BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);

keyMapper: key的映射,valueMapper:value的映射,

mergeFunction: 当value冲突时,调用的value合并的方法,mapSupplier map的构造器

例子1:

toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);

//根据name转成map
userList.stream().collect(Collectors.toMap(User::getName, t -> t))
    .forEach((k,v) -> System.out.println("k1—>"+k+"v1->"+v.toString()));

输出:

k1—>李四v1->User(id=3, name=李四, age=12, salary=10)
k1—>张三v1->User(id=1, name=张三, age=10, salary=10)
k1—>王五v1->User(id=4, name=王五, age=10, salary=10)
k1—>陈七v1->User(id=5, name=陈七, age=10, salary=10)
k1—>赵六v1->User(id=2, name=赵六, age=14, salary=10)

例2:

toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator mergeFunction);

//根据age将salary汇总
userList.stream().collect(Collectors.toMap(User::getAge, Function.identity(),(t1,t2) -> {
    t1.setSalary(t1.getSalary().add(t2.getSalary()));
    t1.setName(t1.getName()+"-"+t2.getName());
    return t1;
})).forEach((k,v) -> System.out.println("k—>"+k+"v->"+v.toString()));

输出:

k—>10v->User(id=1, name=张三-王五-陈七, age=10, salary=30)
k—>12v->User(id=3, name=李四, age=12, salary=10)
k—>14v->User(id=2, name=赵六, age=14, salary=10)

例3:

toMap(Function<? super T, ? extends K> keyMapper,
      Function<? super T, ? extends U> valueMapper,
      BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);
//根据 id 进行排序
userList.stream().collect(
    Collectors.toMap(User::getId, Function.identity(), (t1, t2) -> t1, TreeMap::new)
).forEach((k,v) -> System.out.println("k3—>"+k+"v3->"+v.toString()));

输出:

k—>1v->User(id=1, name=张三, age=10, salary=10)
k—>2v->User(id=2, name=赵六, age=14, salary=10)
k—>3v->User(id=3, name=李四, age=12, salary=10)
k—>4v->User(id=4, name=王五, age=10, salary=10)
k—>5v->User(id=5, name=陈七, age=10, salary=10)

joining 字符串符号拼接

​ 将字符串集合铜通过指定字符去拼接,提供两种函数,如下:

Collector<CharSequence, ?, String> joining(CharSequence delimiter);
// 拼接字 前缀 后缀
Collector<CharSequence, ?, String> joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

例:

Assertions.assertEquals(userList.stream().map(User::getName)
                        .collect(Collectors.joining("%")),"张三%李四%王五%赵六%陈七");
String collect = userList.stream().map(User::getName)
    .collect(Collectors.joining("%", "prefix", "suffix"));
Assertions.assertEquals(collect,"prefix张三%李四%王五%赵六%陈七suffix");

maxBy\minBy 取最大最小
Assertions.assertEquals(
    userList.stream().max(Comparator.comparingInt(User::getAge)).get().getName(),"赵六");
Assertions.assertEquals(
    userList.stream().min(Comparator.comparingInt(User::getAge)).get().getName(),"张三");

summarizing 汇总

​ 汇总一个属性的运算,有summarizingDoublesummarizingLongsummarizingInt。与MaxBy\MinBy的区别是这个只能处理单个属性的。其入参是一个Function。我们可以从其结果中取出平均数Average、个数Count、最大值Max、最小值Min、和Sum

summarizingInt(ToIntFunction<? super T> mapper)

例:

IntSummaryStatistics collect = Stream.of(1,2,3,4,5,6,7).collect(Collectors.summarizingInt(t -> t));
Assertions.assertEquals(collect.getAverage(),4);
Assertions.assertEquals(collect.getCount(),7);
Assertions.assertEquals(collect.getMax(),7);
Assertions.assertEquals(collect.getMin(),1);
Assertions.assertEquals(collect.getSum(),28);

averaging 平均数 counting 数量

averaging 相当于 summarizing getAverage函数,

counting 相当于summarizing getCount函数,不过其不是算的某个字段而是所有。

Assertions.assertEquals(userList.stream().collect(Collectors.averagingInt(User::getAge)),11.2);
Assertions.assertEquals(Stream.of(1, 2, 3, 4, 5, 6, 7).collect(Collectors.counting()),7);

groupingBy 分组

​ 将流根据指定属性进行分组,与toMap不一样的地方在于其value是一个Collectors可以做更多的逻辑。提供以下三种函数供使用:

//根据指定属性进行分组
Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> function);
//根据指定属性进行分组,将value使用collector进行处理
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> function,Collector<? super T, A, D> collector);
//可以自行指定输出的map类型
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                              Supplier<M> mapFactory,
                              Collector<? super T, A, D> downstream) 

例:

//根据指定属性 age 去分组
Map<Integer, List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getAge));
userMap.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
//根据指定属性 name+age 去分组
System.out.println("-------------条件重定义:根据指定属性name+age去分组-------------");
Map<String, List<User>> userMap1 = userList.stream().collect(
    Collectors.groupingBy(t -> t.getName() + "&" + t.getAge()));
userMap1.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
//条件重定义。根据age大于10和小于等于10分组
System.out.println("-------------条件重定义:根据age大于10和小于等于10分组-------------");
Map<String, List<User>> userMap2 = userList.stream().collect(
    Collectors.groupingBy(t -> {
        if (t.getAge() > 10) {
            return "青少年";
        }
        return "少年";
    }));
userMap2.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
//多条件分组. 根据age大于10和小于等于10分组,再获得各自的男女
System.out.println("-------------多条件分组:根据age大于10和小于等于10分组-------------");
Map<String, Map<String, List<User>>> userMap3 = userList.stream().collect(
    Collectors.groupingBy(
        //条件1:根据age大于10和小于等于10分组
        t -> {
            if (t.getAge() > 10) {
                return "青少年";
            }
            return "少年";
        },
        //条件2:根据男女再次分组
        Collectors.groupingBy(User::getSex)
    )
);
userMap3.forEach((k1, v) -> {
    v.forEach((k2, v2) -> System.out.println("k1->" + k1 + ",k2->" + k2 + ",v->" + v2));
});
//结果集的聚合函数,判断男女的人数
System.out.println("-------------结果集的聚合函数,判断男女的人数-------------");
Map<String, Long> userMap4 = userList.stream().collect(
    Collectors.groupingBy(User::getSex, Collectors.counting()
);
userMap4.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
//提供指定的Map构造函数:根据age分组并根据age升序
System.out.println("-------------提供指定的Map构造函数:根据age分组并根据age降序-------------");
TreeMap<Integer, List<User>> userMap5 = userList.stream().collect(
    Collectors.groupingBy(User::getAge,
                          //根据age降序
                          () -> new TreeMap<Integer, List<User>>(Comparator.comparing(t-> -t)),
                          Collectors.toList()));
userMap5.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));

输出:

k->6,v->[User(id=5, name=陈七, age=6, salary=10, sex=男)]
k->10,v->[User(id=1, name=张三, age=10, salary=10, sex=男), User(id=4, name=王五, age=10, salary=10, sex=女)]
k->12,v->[User(id=3, name=李四, age=12, salary=10, sex=女)]
k->14,v->[User(id=2, name=赵六, age=14, salary=10, sex=女)]
-------------条件重定义:根据指定属性name+age去分组-------------
k->王五&10,v->[User(id=4, name=王五, age=10, salary=10, sex=女)]
k->李四&12,v->[User(id=3, name=李四, age=12, salary=10, sex=女)]
k->赵六&14,v->[User(id=2, name=赵六, age=14, salary=10, sex=女)]
k->张三&10,v->[User(id=1, name=张三, age=10, salary=10, sex=男)]
k->陈七&6,v->[User(id=5, name=陈七, age=6, salary=10, sex=男)]
-------------条件重定义:根据age大于10和小于等于10分组-------------
k->青少年,v->[User(id=3, name=李四, age=12, salary=10, sex=女), User(id=2, name=赵六, age=14, salary=10, sex=女)]
k->少年,v->[User(id=1, name=张三, age=10, salary=10, sex=男), User(id=4, name=王五, age=10, salary=10, sex=女), User(id=5, name=陈七, age=6, salary=10, sex=男)]
-------------多条件分组:根据age大于10和小于等于10分组-------------
k1->青少年,k2->女,v->[User(id=3, name=李四, age=12, salary=10, sex=女), User(id=2, name=赵六, age=14, salary=10, sex=女)]
k1->少年,k2->女,v->[User(id=4, name=王五, age=10, salary=10, sex=女)]
k1->少年,k2->男,v->[User(id=1, name=张三, age=10, salary=10, sex=男), User(id=5, name=陈七, age=6, salary=10, sex=男)]
k->女,v->3
k->男,v->2

partitioningBy 根据true\false分组

例:

//根据指定属性 age 去分组
Map<Boolean, List<Integer>> map = Stream.of(1, 2, 3, 4, 5, 6, 7).collect(
    Collectors.partitioningBy(t -> t % 2 == 0));
Assertions.assertEquals(map.get(true).toString(),Lists.newArrayList(2,4,6).toString());

mapping 将指定的属性取出,带入后续做collectors的传入
 Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,Collector<? super U, A, R> downstream)

例:

String collect = userList.stream().collect(Collectors.mapping(User::getSex, Collectors.joining("&")));
Assertions.assertEquals(collect,"男&女&女&女&男");

reducing 数据归约

​ 将流的节点通过规则进行聚合,提供如下三种函数

//传入一个BiFunction,其元素1的元素将会带入到后续中进行计算
Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) 
//传入一个默认元素,默认元素将会带入到后续进行计算
Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op)
//传入一个默认元素,传入一个 mapper 其会将节点中的每个元素都参与处理,之后会带入到后续进行计算
Collector<T, ?, U> reducing(U identity,
                            Function<? super T, ? extends U> mapper,
                            BinaryOperator<U> op)

例1:

​ 算出所有元素的累计之和

Collector<T, ?, Optional> reducing(BinaryOperator op)

Assertions.assertEquals(Stream.of(1,2,3,4,5,6).collect(Collectors.reducing((t1,t2) -> t1+t2)).get(),21);

过程分析:

1,2 -> return 1+2

3,3 -> return 3+3

6,4 -> return 6+4

.....

例2:

​ 传入一个默认元素然后将其带入到后面进行运算

​ Collector<T, ?, T> reducing(T identity, BinaryOperator op)

 Assertions.assertEquals(
     Stream.of(1,2,3,4,5,6).collect(Collectors.reducing(100,(t1,t2) -> t1+t2)),121)

例3:

​ 传入一个默认元素,传入一个 mapper 其会将节点中的每个元素都参与处理,之后会带入到后续进行计算

Collector<T, ?, U> reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator op)

Assertions.assertEquals(
    Stream.of(1,2,3,4,5,6).collect(Collectors.reducing(0,t -> t+1,(t1,t2) -> t1+t2)),27);

过程分析:

identity : 获得一个默认值 0

mapper: 将1 带入得 2

op: 0,2 -> return 0+2

mapper: 将2 带入得 3

op:2,3 -> return 2+3

mapper: 将3 带入得 4

op:5,4 -> return 5+4

。。。

collectingAndThen 归集然后再操作

​ 将流的元素通过collectors转化以后再通过Function进行操作,提供如下函数

Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher)

例1:

​ 汇总 少年和青年男女分别的薪水总额

Map<String, Double> result = userList.stream().collect(Collectors.collectingAndThen(
    Collectors.groupingBy(t -> {
        if (t.getAge() > 10) {
            return "青少年";
        }
        return "少年";
    }, Collectors.groupingBy(User::getSex)),
    //Map<String, Map<String, List<User>>>
    e -> {
        Map<String, Double> map = Maps.newHashMap();
        e.forEach((k, v) -> {
            //每一组用户
            v.forEach((k1, v1) -> {
                double sum = v1.stream().map(User::getSalary).collect(Collectors.summarizingDouble(BigDecimal::doubleValue)).getSum();
                map.put(k + "-" + k1, sum);
            });
        });
        return map;
    }
));
result.forEach((k, v) -> System.out.println("k->" + k + "v->" + v));

结果:

k->少年-男v->20.0
k->青少年-男v->40.0
k->青少年-女v->30.0

例2:

​ 男女根据年龄age降序

Map<String, List<User>> collect = userList.stream().collect(Collectors.collectingAndThen(
    Collectors.groupingBy(User::getSex),
    e -> {
        e.values().forEach(t -> t.sort(Comparator.comparing(User::getAge).reversed()));
        return e;
    }
));
collect.forEach((k,v) -> v.forEach(v1 -> System.out.println("k->" + k + "v->" + v1)));

结果:

k->女v->User(id=4, name=王五, age=18, salary=10, sex=女)
k->女v->User(id=2, name=赵六, age=14, salary=10, sex=女)
k->女v->User(id=3, name=李四, age=12, salary=10, sex=女)
k->男v->User(id=5, name=洪税, age=43, salary=10, sex=男)
k->男v->User(id=5, name=张三丰, age=23, salary=10, sex=男)
k->男v->User(id=5, name=张无忌, age=16, salary=10, sex=男)
k->男v->User(id=5, name=洪七公, age=14, salary=10, sex=男)
k->男v->User(id=1, name=张三, age=10, salary=10, sex=男)
k->男v->User(id=5, name=陈七, age=6, salary=10, sex=男)

例3:

​ 去重只保留对应的age的用户

List<User> collect1 = userList.stream().collect(Collectors.collectingAndThen(
    Collectors.toMap(User::getAge, identity(), (t1, t2) -> t1, Maps::newHashMap),
    e -> Lists.newArrayList(e.values())
));
collect1.forEach(System.out::println);

结果:

User(id=5, name=张无忌, age=16, salary=10, sex=男)
User(id=4, name=王五, age=18, salary=10, sex=女)
User(id=5, name=陈七, age=6, salary=10, sex=男)
User(id=5, name=张三丰, age=23, salary=10, sex=男)
User(id=1, name=张三, age=10, salary=10, sex=男)
User(id=5, name=洪税, age=43, salary=10, sex=男)
User(id=3, name=李四, age=12, salary=10, sex=女)
User(id=2, name=赵六, age=14, salary=10, sex=女)

min | max

用法:

Optional opt = stream.min((v1,v2) -> return Integer);
Optional opt = stream.max((v1,v2) -> return Integer);

e.g.:

Stream 中只要有一个元素符合传入的 predicate,返回 trueEngineer engineerMin = engineers.stream().min((e1, e2) -> e1.getAge() - e2.getAge()).get();
System.out.println("[min]获得最小 ==>"+engineerMin);
Engineer engineerMax = engineers.stream().max((e1, e2) -> e1.getAge() - e2.getAge()).get();
System.out.println("[max]获得最大 ==>"+engineerMax);

返回结果:

[min]获得最小 ==>Engineer{name='李四', age=14, salary=5000}
[max]获得最大 ==>Engineer{name='赵六', age=20, salary=20000}

anyMatch

用法:

stream.anyMatch(T t -> {return true|false});

Stream 中只要有一个元素符合传入的 Predicate 表达式,则返回 true

e.g.:

final String engineerName="张三";
boolean b = engineers.stream().anyMatch(engineer ->  engineerName.equals(engineer.getName()));
if (b) {
    System.out.println("["+engineerName+"]在集合中");
}

结果:

[张三]在集合中

allMatch | noneMatch

用法:

stream.allMatch(enginner -> {return true|false});//全为true则返true,否者为false
stream.noneMatch(enginner -> {return true|false});//全为false则返回true,否者为true

e.g.:

Map engineerMap = new HashMap<String,Engineer>(engineers.size());
engineers.forEach(engineer -> engineerMap.put(engineer.getName(),engineer));
Set<String> SetKey =  engineerMap.keySet();
boolean b = engineers.parallelStream().allMatch(engineer -> SetKey.contains(engineer.getName()));
if (b) {
    System.out.println("集合所有元素在新集合中都拥有");
}
//重置map
Set<String> finalSetKey =  new HashMap<String,Engineer>(engineers.size()).keySet();
 b = engineers.parallelStream().noneMatch(engineer -> finalSetKey.contains(engineer.getName()));
if (b) {
    System.out.println("集合所有元素在新集合中都[不]拥有");
}

结果:

集合所有元素在新集合中都拥有
集合所有元素在新集合中都[不]拥有

findFirst | findAny

用法:

Optional opt = stream.findFirst();//获得流的第一个元素返回Optional 
Optional opt = stream.findAny();//获得流(并行流会有变化)的任意元素返回Optional 

e.g.:

Optional<Engineer> first = engineers.parallelStream().findFirst();
System.out.println("使用[findFirst] ==>"+first.get());
Optional<Engineer> any = engineers.parallelStream().findAny();
System.out.println("使用[findAny] ==>"+any.get());

结果:

使用[findFirst] ==>Engineer{name='李四', age=14, salary=5000}
使用[findAny] ==>Engineer{name='王五', age=15, salary=3000}

Short-circuiting:

操作 含义
anyMatch 一个符合则跳出为True
noneMatch 全部为false则为True
findAny 获得任一
allMatch 全部为true则为true
findFirst 获得第一个
limit 截取前n个

Optional

对象校验的一个包装类,包地址 java.util.Optional

ofNullable | of

Optional 参数的创建需要时用 ofNullable 或者of 方法来创建,两者都会返回一个 Optional 对象。其区别是:

ofNullable 在校验对象为空时不会抛错,常使用此方式来创建Optional对象,而 of 会直接返回 NullPointerException 异常

e.g.:

Engineer engineer = null;
Optional<Engineer> engineer1 = Optional.ofNullable(engineer);
Optional<Engineer> engineer2 = Optional.of(engineer);

结果:

java.lang.NullPointerException
	at java.util.Objects.requireNonNull(Objects.java:203)
	at java.util.Optional.<init>(Optional.java:96)
	at java.util.Optional.of(Optional.java:108)
    ...

get

获取Optional的值

e.g.:

Engineer engineer = new Engineer("李四", 14, new BigDecimal("5000"));
Optional<Engineer> engineer1 = Optional.ofNullable(engineer);
System.out.println(engineer1.get());

结果:

Engineer{name='李四', age=14, salary=5000}

ifPresent

如果 Optional 有效则会执行ifPresent 中的方法

optional.ifPresent(T t -> {});//此处的t 是校验对象

e.g.:

Engineer engineer = new Engineer("李四", 14, new BigDecimal("5000"));
Optional.ofNullable(engineer).ifPresent(engineer1 ->  engineer.setAge(20));
System.out.println(engineer);

结果:

Engineer{name='李四', age=20, salary=5000}

orElse

optional 校验对象为空时,其会执行此方法里面的内容,其返回值要与校验对象一致

optional.orElse(return T t);

e.g.:

Engineer engineer = null;
engineer = Optional.ofNullable(engineer).orElse(new Engineer("李四", 14, new BigDecimal("5000")));
System.out.println(engineer);

结果:

Engineer{name='李四', age=14, salary=5000}

orElseGet

optional 校验对象为空时,其会执行此方法里面的内容,其返回值要与校验对象一致.

OrElse 的区别是,orElseGet 接一个lambda表达式,表达式默认不执行只在需要的时候被执行,而 orElse 会注定执行

optional.ofElseGet(Supplier sup);//接Supplier 函数接口

e.g.:

Engineer engineer = null;
engineer = Optional.ofNullable(engineer).orElseGet(() -> new Engineer("张三", 12, new BigDecimal("20.5")));
System.out.println(engineer);

结果:

Engineer{name='张三', age=12, salary=20.5}

orElseThrow

前者没达到得情况下会激活抛出错误.

optional.orElseThrow(() -> return Exception);

e.g.:

Engineer engineer = null;
engineer = Optional.ofNullable(engineer)
			.orElseThrow(
				() -> new RuntimeException("报错了哦[空指针]!")
			);
System.out.println(engineer);

结果:

java.lang.RuntimeException: 报错了哦[空指针]!
	at OptionalTest.lambda$test2$1(OptionalTest.java:44)
	at java.util.Optional.orElseThrow(Optional.java:290)

map

filter

过滤器,传入一个 Predicate 函数接口,成功则会成功返回Optional,否者返回一个空的 Optional

Optional opt = optional.filter(e -> {return true|false});

e.g.:

ArrayList statusList = new ArrayList<String>();
statusList = Optional.ofNullable(statusList)
     //返回一个 null的optional
        .filter(statuses -> !(statuses == null || statuses.isEmpty()))
    //因为上文返回的null 的optional 则 orElseGet 会被执行填入参数
        .orElseGet(() -> {
            ArrayList arrayList = new ArrayList();
            arrayList.add("张三");
            arrayList.add("李四");
            return arrayList;
        });
System.out.println(statusList);

结果:

[张三, 李四]
posted @ 2021-01-25 15:42  追风的独角鲸  阅读(85)  评论(0编辑  收藏  举报