函数式编程

FunctionalInterface 我们把只定义了单方法的接口称之为FunctionalInterface,用注解@FunctionalInterface标记。例如,Callable接口:

@FunctionalInterface public interface Callable { V call() throws Exception; }

支持函数式编程的都可以使用Lambda表达式
Lambda表达式 当我们用Arrays.sort()排序时,可以传入一个Comparator实例,并采用匿名类的方式来实现:
String[] array = ... Arrays.sort(array, new Comparator() { public int compare(String s1, String s2) { return s1.compareTo(s2); } });

但是这种写法还是挺繁琐的,所以从JDK8开始,我们可以用Lambda表达式来替代这种繁琐的写法:
String[] array = ... Arrays.sort(array, (s1, s2) -> { return s1.compareTo(s2); });

使用Lambda只需要写出方法的定义,参数类型可以省略,编译器会自动推断出String类型,-> { ... }表示方法体,如果只有一行return代码,还可以更加简洁:
Arrays.sort(array, (s1, s2) -> s1.compareTo(s2));

返回值的类型也是由编译器自动推断的,这里推断出的返回值是int,因此,只要返回int,编译器就不会报错。
方法引用 除了使用Lambda之外,还可以直接传入方法引用:
public static void main(String[] args) { String[] array = new String[]{"Apple", "Orange", "Banana", "Lemon"}; Arrays.sort(array, Main::cmp); System.out.println(String.join(", ", array)); }static int cmp(String s1, String s2) { return s1.compareTo(s2); }

上面的代码是啥意思?可以看到Arrays.sort需传入一个数组和Comparator接口,在Comparator中有个方法int compare(T o1, T o2),我们自己定义的方法cmpcompare这个方法的方法签名一致,即方法参数和返回类型相同,就可以直接使用方法引用,再看一个例子:
public static void main(String[] args) { String[] array = new String[]{"Apple", "Orange", "Banana", "Lemon"}; Arrays.sort(array, String::compareTo); System.out.println(String.join(", ", array)); }

查看StringcompareTo方法发现参数只有一个,但是前面不是说方法签名要一致吗,这又是怎么回事?因为之前的方法是个静态方法,这里是一个实例方法,实例方法第一个隐含参数总是传入this,相当于:
public static int compareTo(this, String o);

所以String::compareTocompare方法签名是一致的
Stream 一个全新的流失API,可以存储有限或无限个元素,Stream是惰性计算,计算通常时发生在最后结果的获取,因此,Stream API的基本用法就是:创建一个Stream,然后做若干次转换,最后调用一个求值方法获取真正计算的结果:
如何创建一个Stream?
Stream.of
Stream stream = Stream.of("A", "B", "C", "D");

数组或Collection
Stream stream = Arrays.stream(new String[]{"A", "B", "C"}); Stream stream1 = List.of("A", "B", "C").stream();

Supplier
通过Stream.generate()需传入Supplier对象
public static Stream generate(Supplier s) { Objects.requireNonNull(s); return StreamSupport.stream( new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false); }

Supplier是一个函数式接口,我们可以自己实现这个接口
@FunctionalInterface public interface Supplier {/** * Gets a result. * * @return a result */ T get(); }

class NatualSupplier implements Supplier {int n = 0; @Override public Integer get() { return n++; } }

基于Supplier创建的Stream会不断调用get()产生下一个元素,可以用来表示无限序列
public static void main(String[] args) { Stream stream = Stream.generate(new NatualSupplier()); stream.limit(20).forEach(System.out::println); }

因为它会不断调用get(),我们必须设定一个界限stream.limit(20)
基本类型
因为Java的泛型不支持基本类型的,只能用Integer等包装类型,但是Stream会对频繁的拆箱装箱,所以为了提高效率,Java标准库给我们提供了三种使用基本类型的Stream -> IntStreamLongStreamDoubleStream
map
Stream.map()是一个转换方法,将一个Stream转为另一个Stream
Stream stream = List.of(1, 2, 3).stream(); Stream streamMap = stream.map(item -> item * item);

我们看看map(),最终会返回一个新结果的Stream
/** * Returns a stream consisting of the results of applying the given * function to the elements of this stream. * * This is an intermediate * operation. * * @param The element type of the new stream * @param mapper a non-interfering, *stateless *function to apply to each element * @return the new stream */ Stream map(Function mapper);

Stream.map()传入的是函数式接口Functionapply()最终return计算的结果
@FunctionalInterface public interface Function {/** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); ... }

filter
Stream.filter()Stream的另一个常用转换方法
filter即过滤,过滤掉不满足条件的元素,满足条件的构成一个新的Stream
Stream stream = List.of(3, 4, 6).stream(); Stream streamMap = stream.filter(item -> item % 2 == 0);

filter接收Predicatetest()过滤掉不满足条件的元素
@FunctionalInterface public interface Predicate {/** * Evaluates this predicate on the given argument. * * @param t the input argument * @return {@code true} if the input argument matches the predicate, * otherwise {@code false} */ boolean test(T t); }

reduce
Stream的一个聚合方法,把一个Stream的所有元素按照聚合函数聚合成一个结果
Stream stream = List.of(3, 4, 6).stream(); Integer reduce = stream.reduce(0, (acc, n) -> acc + n);

Stream.reduce()接收BinaryOperator,而它又继承自BiFunction,在BiFunction中有R apply(T t, U u)
/** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u);

所以BinaryOperator实际上是重写了父接口的方法apply(),通过这个方法进行累加计算
第一个参数0相当于初始值,见源码注释:
*
{@code *T result = identity; *for (T element : this stream) *result = accumulator.apply(result, element) *return result; * }


输出Stream
Streammap()filter()操作时是不会进行任何计算的,reduce会立即得出结果
如何把进行了转换操作的元素保存下来呢?
输出为集合:
collect()并传入Collectors.toList()对象:
Stream stream = List.of(3, 4, 6).stream(); List list = stream.map(n -> n * n).collect(Collectors.toList());

输出为数组:
Stream stream = List.of("Apple", "Banana", "Pear").stream(); String[] array = stream.toArray(String[]::new);

输出为Map:
Stream stream = List.of("Apple:Banana", "Pear:Peach").stream(); Map map = stream.collect(Collectors .toMap( // 映射为key s -> s.substring(0, s.indexOf(':')), // 映射为value s -> s.substring(s.indexOf(':') + 1) ));

分组输出:
Stream stream = List.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado", "Cherry", "Apricots").stream(); Map> groups = stream.collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList() ));

【函数式编程】上述代码中Collectors.groupingBy以元素的首字母为依据做一个分组

    推荐阅读