函数式编程
OO
(object oriented
,面向对象)是抽象数据,FP
(functional programming
,函数式编程)是抽象行为。
在《Java 8 实战》中,提到过一个概念——行为参数化,和这里的抽象行为有异曲同工之妙。
Lambda表达式
基本语法:
- 参数。
- 一个参数,不需要括号
()
;两个以上参数,使用括号()
包裹参数。 - 没有参数,使用括号
()
表示空参数列表。
- 一个参数,不需要括号
- 接着
->
,可视为“产出”。 ->
之后的内容都是方法体。- 单行不需要{ }包裹;多行需要{}包裹。
// 无参数
() -> System.out.println("hello")
// 单参数
(a) -> System.out.println(a + "hello")
// 多参数
(a,b) -> System.out.println(a + b + "hello")
// 多行方法体
(a,b) -> {
System.out.println(a + b + "hello");
return a + b;
}
方法引用
类名或对象名::方法名称
函数式接口
Java 8 引入了 java.util.function
包。包中的接口,是 Lambda 表达式和方法引用的目标类型。 每个接口只包含一个抽象方法,称为函数式方法。
包中创建了一组完整的目标接口,让我们一般情况下不需再定义自己的接口。
使用@FunctionalInterface
注解,表明该接口是函数式接口,并强制接受检查,接口内只能存在一个抽象方法。
function
基本命令规则:
-
如果只处理对象(非基本类型),名称为
Function
,Consumer
,Predicate
,Supplier
等。参数类型通过泛型添加。这是基本类型,后面基本都是其变体。
-
如果接收参数是基本类型,则由名称的第一部分表示。如
LongConsumer
,DoubleFunction
,IntPredicate
等,但返回基本类型的Supplier
接口例外。 -
如果返回值为基本类型,则用
To
表示,如ToLongFunction <T>
和IntToLongFunction
。 -
如果返回值类型与参数类型一致,则表明是一个运算符:单个参数使用
UnaryOperator
,两个参数使用BinaryOperator
。 -
如果接收两个参数且返回值为布尔值,则是一个谓词(Predicate)。
-
如果接收的两个参数类型不同,则名称中有一个
Bi
。
用基本类型的唯一原因是,可以避免传递参数和返回结果过程中的自动装箱和自动拆箱,进而提升性能。
高阶函数
消费或产生函数的函数。
// 仅仅相当于把Function函数,换了个名字
interface FuncSS extends Function<String, String> {}
public class ProduceFunction {
// produce()被称为高阶函数
static FuncSS produce() {
return s -> s.toLowerCase(); // 返回一个函数
}
public static void main(String[] args) {
FuncSS f = produce();
System.out.println(f.apply("YELLING"));
}
}
闭包
看完也没明白,闭包是要干什么……
函数组合
函数组合(Function Composition)意为“多个函数组合成新函数”。
一般使用andThen()
、compose()
、and()
、or()
等等方法进行组合。
柯里化和部分求值
将一个多参数的函数,转换为一系列单参数函数。这个也不是很明白。
public class CurryingAndPartials {
// 未柯里化:
static String uncurried(String a, String b) {
return a + b;
}
public static void main(String[] args) {
// 柯里化的函数:
Function<String, Function<String, String>> sum =
a -> b -> a + b; // 第二个参数是另一个函数。
// 输出 Hi Ho
System.out.println(uncurried("Hi ", "Ho"));
Function<String, String> hi = sum.apply("Hi "); // [2]
//输出 Hi Ho
System.out.println(hi.apply("Ho"));
// 部分应用:
Function<String, String> sumHi = sum.apply("Hup ");
// Hup Ho
System.out.println(sumHi.apply("Ho"));
// Hup Hey
System.out.println(sumHi.apply("Hey"));
}
}
流式编程
集合优化了对象的存储,而流和对象的处理有关。
流创建
-
Stream.of()
Stream<Integer> stream = Stream.of(1, 2, 3, 4);
-
集合调用
stream()
List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream();
-
随机数流
Random
中也新增了生成流的方法。Stream<Integer> stream2 = new Random(50).ints().boxed();
-
整型序列流
IntStream stream3 = IntStream.range(1, 10);
-
generate()
@FunctionalInterface public interface Supplier<T> { T get(); } public class Generator implements Supplier<String> { Random rand = new Random(47); char[] letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); // 实现了函数式接口的方法 public String get() { return "" + letters[rand.nextInt(letters.length)]; } public static void main(String[] args) { // 传入一个提供者类型的接口 String word = Stream.generate(new Generator()) .limit(30) .collect(Collectors.joining()); System.out.println(word); } }
-
iterate()
Stream.iterate()
产生的流的第一个参数是种子,然后将种子传递给第二个参数(第二个参数是一个方法)。运算结果,存储起来作为下次调用iterate()
时的第一个参数。// 斐波那契数列public class Fibonacci { int x = 1; Stream<Integer> numbers() { return Stream.iterate(0, i -> { int result = x + i; x = i; return result; }); }}
-
流的建造者模式
Stream.Builder<Integer> builder = Stream.builder();builder.add(1).add(2).add(3);Stream<Integer> stream4 = builder.build();
-
Arrays
String[] arrays = new String[] {"1","2","3"};Stream<String> stream5 = Stream.of(args);
-
正则表达式
String str = "a.b.c,f";String s = Pattern.compile("[ .,?]+") .splitAsStream(str) .map(a -> a + " ") .collect(Collectors.joining());System.out.println(s);
中间操作
跟踪调试
peek()
操作的目的是帮助调试,接收一个Consumer
函数式接口。
排序
sorted()
Stream<Integer> stream2 = new Random(50).ints(10,1000).boxed();stream2 .skip(10) .limit(10) .sorted(Comparator.comparing(Integer::intValue)) .map(a -> a + " ") .forEach(System.out::print);// 18 105 132 147 192 405 480 602 760 892
移除
移除元素有两种方法,去重和过滤。
-
distinct()
:消除流中的重复元素。Stream<Integer> stream = Stream.of(1, 2, 3, 4, 2, 3);List<Integer> collect = stream.distinct().peek(e -> System.out.println(e)).collect(Collectors.toList());// 1 2 3 4
-
filter(Predicate)
:predicate()
为true
,保留元素。Stream<Integer> stream = Stream.of(1, 2, 3, 4, 2, 3);List<Integer> collect1 = stream.filter(a -> a.equals(2)).peek(e -> System.out.println(e)).collect(Collectors.toList());//2 2
函数
map(Function)
:将函数操作应用在输入流的元素中,并将返回值传递到输出流中。mapToInt(ToIntFunction)
:操作同上,但结果是IntStream
。mapToLong(ToLongFunction)
:操作同上,但结果是LongStream
。mapToDouble(ToDoubleFunction)
:操作同上,但结果是DoubleStream
。
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 2, 3);String collect = stream.map(a -> { String aStr = a + ""; aStr += "^_^"; return aStr;}).collect(Collectors.joining(" "));System.out.println(collect);// 1^_^ 2^_^ 3^_^ 4^_^ 2^_^ 3^_^
组合流
把多个流,合并成一个流,也叫扁平化流。
flatMap()
做了两件事:将产生流的函数应用在每个元素上(与 map()
所做的相同),然后将每个流都扁平化为元素,因而最终产生的仅仅是元素。
flatMap(Function)
:当 Function
产生流时使用。
另外还有三个方法,跟map()
的另外三个类似。
// 这是获取流的流Stream.of(1,2,3,4) .map(a -> Stream.of(a+"a",a+"b",a+"c")) .forEach(System.out::println);//java.util.stream.ReferencePipeline$Head@6a41eaa2//java.util.stream.ReferencePipeline$Head@7cd62f43//java.util.stream.ReferencePipeline$Head@6d4b1c02//java.util.stream.ReferencePipeline$Head@6093dd95 Stream.of(1,2,3,4) .flatMap(a -> Stream.of(a+"a",a+"b",a+"c")) .forEach(System.out::println); //1a 1b 1c 2a 2b 2c 3a 3b 3c 4a 4b 4c
Optional 类
一些流操作会返回Optional 对象,这是为了避免空指针异常。
findFirst()
返回一个包含第一个元素的 Optional 对象,如果流为空则返回Optional.empty
findAny()
返回包含任意元素的 Optional 对象,如果流为空则返回Optional.empty
max()
和min()
返回一个包含最大值或者最小值的 Optional 对象,如果流为空则返回Optional.empty
Optional 便利函数
ifPresent(Consumer)
:当值存在时调用 Consumer,否则什么也不做。orElse(otherObject)
:如果值存在则直接返回,否则生成 otherObject。orElseGet(Supplier)
:如果值存在则直接返回,否则使用 Supplier 函数生成一个替代对象。orElseThrow(Supplier)
:如果值存在直接返回,否则使用 Supplier 函数生成一个异常。
Optional 创建方法
empty()
:生成一个空 Optional。of(value)
:将一个非空值包装到 Optional 里。ofNullable(value)
:针对一个可能为空的值,为空时自动生成 Optional.empty,否则将值包装在 Optional 中。
Optional 对象操作
filter(Predicate)
:对 Optional 中的内容应用Predicate 并将结果返回。如果内容不满足 Predicate ,返回空 Optional 。如果 Optional 已经为空,则直接返回空Optional 。map(Function)
:如果 Optional 不为空,则内容执行方法,并返回结果。否则直接返回 Optional.empty。flatMap(Function)
:同map()
,不进行Optional
封装。
Optional 流
// 创建 Optional 流Stream stream = Stream.of(1,null,2,3).map(signal -> Optional.ofNullable(signal)); // 使用 Optional 流stream .limit(10) .filter(Optional::isPresent) .map(Optional::get) .forEach(System.out::println);
终端操作
执行终端操作后,流操作结束。
数组
toArray()
:将流转换成适当类型的数组。toArray(generator)
:在特殊情况下,生成自定义类型的数组。
循环
forEach(Consumer)
常见如System.out::println
作为 Consumer 函数。forEachOrdered(Consumer)
: 保证forEach
按照原始流顺序操作。
parallel()
:可实现多处理器并行操作。实现原理为将流分割为多个(通常数目为 CPU 核心数)并在不同处理器上分别执行操作。
集合
collect(Collector)
:使用 Collector 收集流元素到结果集合中。collect(Supplier, BiConsumer, BiConsumer)
:同上,第一个参数 Supplier 创建了一个新的结果集合,第二个参数 BiConsumer 将下一个元素收集到结果集合中,第三个参数 BiConsumer 用于将两个结果集合合并起来。
组合
reduce(BinaryOperator)
:使用 BinaryOperator 来组合所有流中的元素。因为流可能为空,其返回值为 Optional。reduce(identity, BinaryOperator)
:功能同上,但是使用 identity 作为其组合的初始值。因此如果流为空,identity 就是结果。
匹配
allMatch(Predicate)
:在第一个 false 时,则停止执行计算。anyMatch(Predicate)
:在第一个 true 是停止执行计算。noneMatch(Predicate)
:如果流的每个元素提供给 Predicate 都返回 false 时,结果返回为 true。在第一个 true 时停止执行计算。
查找
findFirst()
:返回第一个流元素的 Optional,如果流为空返回 Optional.empty。findAny(
:返回含有任意流元素的 Optional,如果流为空返回 Optional.empty。
信息
count()
:流中的元素个数。max(Comparator)
:根据所传入的 Comparator 所决定的“最大”元素。min(Comparator)
:根据所传入的 Comparator 所决定的“最小”元素。
数字流信息
average()
:求取流元素平均值。max()
和min()
:数值流操作无需 Comparator。sum()
:对所有流元素进行求和。