目录
函数式接口 SAM (single Abstract Method)
CPS(continuation-passing style)
lamda 表达式
使用 lambda 表达式首先需要一个接口:
函数式接口 SAM (single Abstract Method)
@FunctionInterface //【非必须】,如果加上,编译器会进行校验
public interface Runnerable{ //【必须是】 interface
public abstract void run(); //【必须】单个非默认方法
}
Runnable r = ()->System.out.println("hello"); //对方法进行实现
内置的常用函数式接口
输入 | 返回值 | class | 方法 |
---|---|---|---|
T | R | Function<T,R> | R apply(T t) |
void | T | Supplier<T> | T get() |
T | void | Consumer<T> | void accept(T t) |
void | void | Runnalbe | void run() |
T | Boolean | Predicate<T> | boolean test(T t) |
示例:
// 一个输入,一个返回值:Function<T,R>
private static Function<Integer, String> int2String
= (Integer i) -> {return String.valueOf(i);};
// 语法糖
//只有一个表达式,可省略大括号
private static Function<Integer, String> int2String_1
= (Integer i) -> String.valueOf(i);
//参数类型可推导,可省略
private static Function<Integer, String> int2String_2
= (i) -> String.valueOf(i);
//类型可推导且只有一个参数,可省略参数括号
private static Function<Integer, String> int2String_3
= i -> String.valueOf(i);
private static Supplier<Integer> supplier = () -> 1;
private static Consumer<?> consumer = a -> {};
private static Predicate<?> predicate = a -> true;
函数的调用
lambda 为表达式类型,不可直接调用,必须调用方法才能调用
@FunctionalInterface
public interface Runner {
public abstract void run();
}
public class TestRunner {
public static void main(String[] args) {
Runner runner = ()->System.out.println("this is a test");
runner.run();//调用接口的 run() 方法
}
}
// 输出:this is a test
方法引用
· 静态方法引用 -> 需要告知属于的类 classX :: methodName
· 构造方法引用 -> 需要告知属于的类 classX :: new
· 指定具体实例的方法引用 -> 需要告知属于的实例 instance :: methodName
示例1:静态方法
private Function<Integer, String> staticRef = LambdaBasic::int2StringFn;
public static String int2StringFn(Integer i) {
return String.valueOf(i);
}
System.out.println(staticRef.apply(1));
// 相当于:LambdaBasic.int2StringFn(1)
示例2:构造方法
private static Supplier<LambdaBasic> constructor = LambdaBasic::new;
public LambdaBasic() {}
LambdaBasic lb = constructor.get();//相当于:new LambdaBasic ()
System.out.println(lb.staticRef.apply(1));//LambdaBasic.int2StringFn(1)
示例3:具体实例的方法
private BiFunction<Integer, Integer, String> instanceRef = this::twoint2String;
public String twoint2String(int x, int y) {
return x + " " + y;
}
System.out.println(instanceRef.apply(1,2));
// 相当于:LambdaBasic.twoint2String(1,2)
/*
* @param <T> the type of the first argument to the function
* @param <U> the type of the second argument to the function
* @param <R> the type of the result of the function
*/
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
示例4:指定类型任意实例方法引用【特殊】
public class T{
// ……
tMethod(arg2...argN)
// ……
}
public interface FN{
apply(T arg1, arg2...argN)
}
Function f(T::tMethod)
f.apply(Tinstance, arg2...argN)
// 相当于:
Tinstance.tMethod(arg2...argN)
// 最常用的例子:
class A{
Stringg getId();
}
Function f = A::getId();
f.apply(new A());//相当于:new A().getId()
CPS(continuation-passing style)
void fn1(...arg, Consumer callbakc){
res = arg....;
callback.accept(res);
//相当于回调函数,将入参结果作为参数传给回调函数
}
public class CPSSample {
public static int times (int v1, int v2) {
return v1 * v2;
}
public static void add (int v1, int v2, Consumer<Integer> kont) {
int ret = v1 + v2;
kont.accept(ret);
}
public static void square (int v1, Consumer<Integer> kont) {
kont.accept(v1 * v1);
}
public static void main(String[] args) {
add(1,2, ret1->{ // 将入参1,2的计算结果,作为参数给 Consumer 回调函数
square(ret1, ret2->{ // 将入参ret1的计算结果,作为参数给回调函数【相当于连续回调】
System.out.println(ret2);
});
});
}
}
Stream
最重要的三个函数:
· filter(Predicate predicate)
· map(Function mapper)
· reduce(U identity, BinaryOperator acc)
reduce 理解:
/**
* reduce(U identity, BinaryOperator acc)
* 此方法用于理解 reduce:具体的计算逻辑是在函数 accumulator.apply()中实现
* @param initValue 初始值
* @param accumulator 一个函数
* @param datas (此次用集合模拟 stream 流输入)
* @return 返回 reduce 计算后的结果
*/
public static <T,R> R reduce(R initValue,
BiFunction<R, T, R> accumulator,
Collection<T> datas){
R ret = initValue;
for (T data : datas) {
ret = accumulator.apply(ret, data);
}
return ret;
}
reduce 实战1:求和
//list 相当于流,作为数据源
public static int sum(Collection<Integer> list) {
return list.stream().reduce(0, (acc, curr) -> acc + curr);
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
System.out.println("sum = " + sum(list));//输出:sum = 21
}
reduce 实战2:求最大/最小值
public static Integer max(Collection<Integer> list) {
return list.stream().reduce(Math::max).orElse(null);
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
System.out.println("max = " + max(list));//输出:max = 7
}
reduce 实战3:串联成字符串
/**
* 串联成字符串
* @param list 相当于流,模拟数据源
* @param delimiter 字符之间的分隔符
* @return 返回包含分隔符的串联起来的字符串
*/
public static String join(Collection<Integer> list, String delimiter) {
return list.stream().map(String::valueOf).reduce((acc, curr)-> acc + delimiter + curr).orElse(null);
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
System.out.println("join = " + join(list, "|"));//输出:join = 2|3|4|7|5
}
reduce 实战4:用 reduce实现map
//R newValue = mapFn.apply(curr) : 通过mapFn.apply(curr)函数实现映射
public static <T,R> List<R> map(List<T> list, Function<T,R> mapFn) {
List<R> ret = new ArrayList<>();
return list.stream().reduce(ret, (acc, curr)->{
R newValue = mapFn.apply(curr);//!!!
acc.add(newValue);
return acc;
},
(list1, list2) -> {list1.addAll(list2); return list1;} );
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
List<Integer> list1 = filter(list, i->i>3);
System.out.println("filter = " + list1); //输出:filter = [4, 7, 5]
}
reduce 实战5:用 reduce实现filter
//p.test(curr) : 通过p.test(curr)函数实现判断过滤
public static <T, R> List<T> filter(List<T> list, Predicate<T> p) {
List<T> ret = new ArrayList<>();
return list.stream().reduce(ret, (acc, curr)->{
if (p.test(curr)) {
acc.add(curr);
}
return acc;
},
(list1, list2) -> {list1.addAll(list2); return list1;} );
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2,3,4,7,5);
List<Integer> list2 = map(list, i->i*2);
System.out.println("map = " + list2); //输出:map = [4, 6, 8, 14, 10]
}
Stream.collect()
colletc 和 reduce 的区别?
reduce 操作不变数据
collect 操作可变数据
主要有两个 API:
collect(Supplier, Accumulator, Combiner)
collect(Collector)
看看 Collector 是什么:
public interface Collector<T, A, R> {
/**
* @返回一个函数,该函数返回一个新的、可变结果的容器
*/
Supplier<A> supplier();
/**
* @返回一个函数,该函数将值折叠到可变结果的容器中
*/
BiConsumer<A, T> accumulator();
/**
* @返回一个函数,该函数将两个部分结果组合为一个组合结果
*/
BinaryOperator<A> combiner();
/**
* @返回一个函数,将中间结果转换为最近结果
*/
Function<A, R> finisher();
...
}
Collector 主要包含了四个方法,可以说 collect(Collector) 是 collect(Supplier, Accumulator, Combiner) 的完整版。
为了方便构造 Collector ,jdk 又提供了一些 Collector API:
• toList / to(Concurrent)Map / toSet / toCollection
• counting / averagingXX / joining /summingXX
• groupBy / partitioningBy
• mapping / reducing
Collect 实战1:
先按编程语言分组,再按程序等级分层,然后返回一个元组<平均工资,程序员列表>
//key:language v:(key:level, [salary, list])
public static Map<String, Map<Integer, Turple<Integer, List<Programmer>>>> classify(List<Programmer> programmers) {
return programmers.stream().collect(
Collectors.groupingBy(Programmer::getLanguage, //程序语言分组
Collectors.groupingBy(Programmer::getLevel, //等级分组
Collectors.collectingAndThen(
Collectors.toList(), //分组后结果生成list
list -> new Turple(list.stream().collect(Collectors.averagingInt(Programmer::getSalary)), list)
//对 list 中指定条件求平均值
)
)
));
}
Collect 实战2:
随机创建程序员列表:
(1) 创建指定个数长度的stream
(2) 对每个数字使用构造函数创建对象
(3) 对每个对象的各个field进行随机赋值
(4) 过滤数据范围以外的元素
(5) 按指定字段排序
(6) 生产到list
public static void main(String[] args) {
List<Programmer> programmers = Util.generate(10);
System.out.println(classify(programmers));
}
public static List<Programmer> generate(int count){
Random random = new Random();
return IntStream.rangeClosed(0, count) // 0..N (1)
.boxed() //装箱,将 int 装箱成 Integer
.map(S2F(Programmer::new))//Programmer::new 的方法签名为 Supplier,map 入参为 Function
// .map 之后,相当于把一个数字转换成为一个对象 (2)
.peek( //给对象添加属性 (3)
apply(Programmer::setLevel, apply(random::nextInt, 3))
//p.setLevel(random.nextInt(3))
.andThen(apply(Programmer::setSalary, apply(random::nextInt, 50000)))
.andThen(apply(Programmer::setName, ()-> "Programmer" + random.nextInt(count*10)))
.andThen(apply(Programmer::setOutput, apply(random::nextInt, 10)))
.andThen(apply(Programmer::setLanguage, apply(Util::randomPick,new String[]{"JAVA","JS","Lisp", "Haskell"})))
)
//.peek(System.out::println)
.filter(p->p.getSalary() >=3000 && p.getLevel() > 0) //(4)
.sorted(Comparator.comparing(Programmer::getLevel)) //(5)
.peek(System.out::println)
.collect(Collectors.toList()) //(6)
;
}
private static <T, INSTANCE> Consumer<INSTANCE> apply(BiConsumer<INSTANCE, T> consumer, Supplier<T> valueSupplier) {
return instance ->consumer.accept(instance, valueSupplier.get());
}
public static <I,O> Supplier<O> apply(Function<I,O> fn, I arg){
return ()-> fn.apply(arg);
}
Optional
参考:理解、学习与使用 JAVA 中的 OPTIONAL【理解、学习与使用 Java 中的 Optional - 张伯雨 - 博客园】
主要作用:
① 解决臭名昭著的空指针异常(NullPointerException)
② 链式调用
static class Person{
private Debit debit;
public Debit getDebit() {return debit;}
public void setDebit(Debit debit) {this.debit = debit;}
}
static class Debit{
private Card card;
public Card getCard() {return card;}
public void setCard(Card card) {this.card = card;}
}
static class Card{
private Bank bank;
public Bank getBank() {return bank;}
public void setBank(Bank bank) {this.bank = bank;}
}
static class Bank{
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
// 一般:每一层都需要判空
public static String getDebitBankName_1(Person person) {
if (person.getDebit()!=null) {
Debit debit = person.getDebit();
if (debit!=null) {
Card card = debit.getCard();
if (card != null) {
Bank bank = card.getBank();
if (bank != null) {
return bank.getName();
}
}
}
return null;
}else {
return null;
}
}
// optional 封装后:相当于装到一个盒子里,对外就是一个盒子,没有空的概念
public static String getDebitBankName_2(Person person) {
return Optional.ofNullable(person.getDebit())
.map(Debit::getCard) // debit->debit.getCard()
.map(Card::getBank)
.map(Bank::getName)
.orElse(null);
}
//进一步解耦(目前我也没完全搞明白)
public static String getDebitBankName_22(Person person) {
Function<Optional, Optional> getCard = Util.opMap(Debit::getCard);
Function<Optional, Optional> getBank = Util.opMap(Card::getBank);
Function<Optional, Optional> getName = Util.opMap(Bank::getName);
return (String) getCard.andThen(getBank).andThen(getName)
.apply(Optional.ofNullable(person.getDebit())).orElse(null);
}
public static <T,R> Function<Optional, Optional> opMap(Function<T, R> fn){
return op -> op.map(fn);
}
函数式编程整体还是比较灵活的,需要多看多练才能掌握,我感觉自己目前也只是一个了解的程度。这里记录一下,提醒自己后面再看看。