简单全面学习java8新特性Stream-API中的Reduce&Collect(三)
涉及知识(参数函数接口)
1. BiFunction
它是一个函数式接口,包含的函数式方法定义如下:
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
与Function接口的不同点在于它接收两个输入,返回一个输出;
而Function接口 接收一个输入,返回一个输出。
注意它的两个输入,一个输出的类型可以是不一样的。
其他方法
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
2. BinaryOperator
实际上就是继承自BiFunction的一个接口
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
上面已经分析了,BiFunction 的三个参数可以是一样的也可以不一样;
而BinaryOperator就直接限定了其三个参数必须是一样的;
因此BinaryOperator与BiFunction的区别就在这。
它表示的就是两个相同类型的输入经过计算后产生一个同类型的输出。
默认实现的两个方法(根据传入的默认比较器)
minBy --返回两个元素中较小的那个
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
maxBy - 返回两个元素中较大的那个
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
这里需要特殊说明一下,这个传入的比较器如果是顺序的
compare(a, b){
return a-b
}
这样子的,结果的大于0 小于0 是以 a-b的结果算出来的,这样子那两个方法minBy,maxBy就是正确的
如果 结果正负是以 b-a 为依据的,那么他们的结果相反
3. BiConsumer
也是一个函数式接口,它的定义如下:
@FunctionalInterface
public interface BiConsumer<T, U> {
/**
* Performs this operation on the given arguments.
*
* @param t the first input argument
* @param u the second input argument
*/
void accept(T t, U u);
/**
* Returns a composed {@code BiConsumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code BiConsumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
可见它就是一个有两个输入参数的Consumer的变种。计算没有返回值。
4. Optional
链接: 简单全面学习JDK1.8新特性之流式编程-SreamAPI(二).
中的Optional
5. Consumer & Function & Predicate
链接: JDK1.8新特性lambda表达式&函数式接口(一).
中的常见函数式接口
6.Supplier
/**
* Represents a supplier of results.
*
* <p>There is no requirement that a new or distinct result be returned each
* time the supplier is invoked.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #get()}.
*
* @param <T> the type of results supplied by this supplier
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
1. reduce 归约汇聚
reduce中文含义是 减少 缩小 ,在这我称之为汇聚,也有人称之为归约
而 stream中的 reduce方法根据一定的规则将Stream中的元素进行计算后返回一个唯一的值。
主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce;
在Stream中reduce共有三种重载,分别是一个参数的,两个参数的,和三个参数的
一个参数的Optional reduce(BinaryOperator accumulator)
源码:
/**
* Performs a <a href="package-summary.html#Reduction">reduction</a> on the
* elements of this stream, using an
* <a href="package-summary.html#Associativity">associative</a> accumulation
* function, and returns an {@code Optional} describing the reduced value,
* if any. This is equivalent to:
* <pre>{@code
* 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();
* }</pre>
*
* but is not constrained to execute sequentially.
*
* <p>The {@code accumulator} function must be an
* <a href="package-summary.html#Associativity">associative</a> function.
*
* <p>This is a <a href="package-summary.html#StreamOps">terminal
* operation</a>.
*
* @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
* <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function for combining two values
* @return an {@link Optional} describing the result of the reduction
* @throws NullPointerException if the result of the reduction is null
* @see #reduce(Object, BinaryOperator)
* @see #min(Comparator)
* @see #max(Comparator)
*/
Optional<T> reduce(BinaryOperator<T> 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();
还有一句比较重要
遍历流的时候 but is not constrained to execute sequentially.
但不限于按顺序执行
还有要注意的是他返回的是 Optional 类型
再看 参数 accumulator ,他是一个 BinaryOperator,前面讲过
这里用的是他的 apply 方法(传入的两个参数跟返回值都是一个类型的,即是流中元素的类型)
举例:
IntStream ints = IntStream.of(1,2,3,8,2,9,4,5,6);
System.out.println(ints.reduce((x, y) -> x + y).getAsInt());
结果
40
两个参数的 T reduce(T identity, BinaryOperator accumulator)
源码
/**
* Performs a <a href="package-summary.html#Reduction">reduction</a> on the
* elements of this stream, using the provided identity value and an
* <a href="package-summary.html#Associativity">associative</a>
* accumulation function, and returns the reduced value. This is equivalent
* to:
* <pre>{@code
* T result = identity;
* for (T element : this stream)
* result = accumulator.apply(result, element)
* return result;
* }</pre>
*
* but is not constrained to execute sequentially.
*
* <p>The {@code identity} value must be an identity for the accumulator
* function. This means that for all {@code t},
* {@code accumulator.apply(identity, t)} is equal to {@code t}.
* The {@code accumulator} function must be an
* <a href="package-summary.html#Associativity">associative</a> function.
*
* <p>This is a <a href="package-summary.html#StreamOps">terminal
* operation</a>.
*
* @apiNote Sum, min, max, average, and string concatenation are all special
* cases of reduction. Summing a stream of numbers can be expressed as:
*
* <pre>{@code
* Integer sum = integers.reduce(0, (a, b) -> a+b);
* }</pre>
*
* or:
*
* <pre>{@code
* Integer sum = integers.reduce(0, Integer::sum);
* }</pre>
*
* <p>While this may seem a more roundabout way to perform an aggregation
* compared to simply mutating a running total in a loop, reduction
* operations parallelize more gracefully, without needing additional
* synchronization and with greatly reduced risk of data races.
*
* @param identity the identity value for the accumulating function
* @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
* <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function for combining two values
* @return the result of the reduction
*/
T reduce(T identity, BinaryOperator<T> accumulator);
其执行原理
T result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
同样有 but is not constrained to execute sequentially.
但不限于按顺序执行
但是他的返回类型就是流的类型 T
只不过这次初始值不在是null 而是第一个参数 identity
IntStream ints = IntStream.of(1,2,3,8,2,9,4,5,6);
//System.out.println("reduce1:"+ints.reduce((x, y) -> x + y).getAsInt());
System.out.println("reduce2:"+ints.reduce(2,(x,y)->x+y));
Stream sSt = Stream.of("1","2","3","8","2","9","4","5","6");
BinaryOperator<String> operator = (x,y)->x.concat(y);
//System.out.println("reduce2:"+sSt.reduce("2",(x,y)->{return x.toString().concat(y.toString());}));
System.out.println(