JDK8 函数式编程【java进阶笔记九】

目录

lamda 表达式

函数式接口 SAM (single Abstract Method)

方法引用

CPS(continuation-passing style)

Stream

Stream.collect()

Optional


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);
}

函数式编程整体还是比较灵活的,需要多看多练才能掌握,我感觉自己目前也只是一个了解的程度。这里记录一下,提醒自己后面再看看。

猜你喜欢

转载自blog.csdn.net/qq_42183414/article/details/123286064