09Lambda、函数式编程、Stream流、方法引用

Lambda、函数式编程、Stream流、方法引用

  1. 小括号内参数的类型可以省略;
  2. 如果小括号内有且仅有一个参,则小括号可以省略;
  3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

1.使用Lambda标准格式(无参无返回)

public interface Cook {
    public abstract void makeFood();
}
public static void main(String[] args) {
    //匿名内部类
    invokeCook(new Cook() {
        @Override
        public void makeFood() {
            System.out.println("吃饭了");
        }
    });
    
    //lambda表达式
    invokeCook(() -> {
        System.out.println("吃饭了");
    });
    
    //改版后
    invokeCook(()->System.out.println("吃饭了"));
}
public static void invokeCook(Cook cook) {
    cook.makeFood();
}

2.使用Lambda标准格式(有参有返回)

public interface Calculator {
    public abstract int calc(int a,int b);
}
 public static void main(String[] args) {
        invokeCalc(120, 130, new Calculator() {
            @Override
            public int calc(int a, int b) {
                return a+b;
            }
        });

	invokeCalc(120,130,(int a,int b)->{
	    return a+b;
	});
		
	//改良
    invokeCalc(120,130,( a, b)-> a+b);
    }
    private static void invokeCalc(int a,int b,Calculator calculator){
        int sum= calculator.calc(a,b);
        System.out.println("结果是:"+sum);
    }

1.使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法

2.使用Lambda必须具有上下文推断

2.自定义函数式接口

1.@FunctionalInterface注解

@FunctionalInterface
public interface MyFunctionalInterface {
	void myMethod();
}

2.自定义函数式接口

@FunctionalInterface
public interface FunctionTest {
    void method();
}

public class FunctionTestImpl implements FunctionTest{
    @Override
    public void method() {

    }
}

public static void show(FunctionTest myInter){
        myInter.method();
    }
    public static void main(String[] args) {
        show(new FunctionTestImpl());

        show(new FunctionTest() {
            @Override
            public void method() {
                System.out.println("使用匿名内部类重写接口中的抽象方法!");
            }
        });
        //函数式接口
        show(()->{
            System.out.println("使用Lambda表达式");
        });
        //升级
        show(()-> System.out.println("使用Lambda表达式"));
    }

3.函数式编程

1.Lambda的延迟执行

只有当级别满足要求的时候,才会进行三个字符串的拼接;否则三个字符串将不会进行拼接。

private static void log(int level, MessageBuilder builder) {
    if (level == 1) {
      	System.out.println(builder.buildMessage());
    }
}

public static void main(String[] args) {
    String msgA = "Hello";
    String msgB = "World";
    String msgC = "Java";

    log(2, () -> {
        System.out.println("Lambda执行!");
        return msgA + msgB + msgC;
    });
}

2.使用Lambda作为参数和返回值

//参数
public static void start(Runnable run){
    //开启线程
    new Thread(run).start();
}

public static void main(String[] args) {
    start(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" 线程开启了!");
        }
    });
//lambda优化
    start(()->System.out.println(Thread.currentThread().getName()+" Lambda线程开启了!"));
}
//返回值
private static Comparator<String> newComparator() {
  	return (a, b) -> b.length() - a.length();
}

public static void main(String[] args) {
    String[] array = { "abc", "ab", "abcd" };
    System.out.println(Arrays.toString(array));
    Arrays.sort(array, newComparator());
    System.out.println(Arrays.toString(array));
}

4.常用函数式接口

1.Supplier接口

java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。

求最大值

public class MaxSupplier {
    public static int getMax(Supplier<Integer> sup){
        return sup.get();
    }
    public static void main(String[] args) {
        int[] arr = {100,15,45,86,45};
        int max2 = getMax(()->{
            int max = arr[0];
            for (int i : arr) {
                if(i> max){
                    max = i;
                }
            }
            return max;
        });
        System.out.println("max2 = " + max2);
    }
}

2.Consumer接口

java.util.function.Consumer<T>接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。

抽象方法:accept

默认方法:andThen

public static void con(String[] str , Consumer<String> con01,Consumer<String> con02){
    //con01.accept(str);
    //con02.accept(str);
    for (String s : str) {
    con01.andThen(con02).accept(s);
    }
}

public static void main(String[] args) {
    String[] str = {"张三,男","李四,女","王五,男"};
    con(str,
            s1-> System.out.print(s1.split(",")[0]),
            s2-> System.out.println(s2.split(",")[1])
    );
}

3.Predicate接口

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate<T>接口。

抽象方法:test

Predicate接口中包含一个抽象方法:boolean test(T t)`。用于条件判断的场景:

默认方法:and与、or或、negate非

筛选姓名长度为4且为女

public static List<String> method(String[] arrat, Predicate<String> pre01, Predicate<String> pre02) {
    ArrayList<String> list = new ArrayList<>();
    for (String s : arrat) {
        boolean bb = pre01.and(pre02).test(s);
        if(bb){
            list.add(s);
        }
    }
    return list;
}

public static void main(String[] args) {
    String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
    List<String> list = method(array,
            s1->s1.split(",")[1].equals("女"),
            s2->s2.split(",")[0].length()==4);
    System.out.println("list = " + list);
}

4.function接口

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

抽象方法:apply

默认方法:andThen

String str = “夏龙,15”;

  1. 将字符串截取数字年龄部分,得到字符串;
  2. 将上一步的字符串转换成为int类型的数字;
  3. 将上一步的int数字累加100,得到结果int数字。
public static int get(String str, Function<String,String> fun01,Function<String,Integer> fun02,Function<Integer,Integer> fun03){
    int integer = fun01.andThen(fun02).andThen(fun03).apply(str);
    return integer;

}
public static void main(String[] args) {
    String str ="夏龙,15";
    int i = get(str,str1->str1.split(",")[1],str2->Integer.parseInt(str2),str3->str3+100);
    System.out.println("i = " + i);
}

5.Stream流

java.util.stream.Stream<T>是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)

获取一个流非常简单,有以下几种常用的方式:

  • 所有的Collection集合都可以通过stream默认方法获取流;
  • Stream接口的静态方法of可以获取数组对应的流。

1.逐一处理:forEach

2.过滤:filter

3.映射:map

4.统计个数:count

5.取用前几个:limit

6.跳过前几个:skip

7.组合:concat

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list.add("张无忌");
    list.add("周芷若");
    list.add("赵敏");
    list.add("张强");
    list.add("张三丰");

    //1filter  2forEach
    list.stream().filter(s->s.startsWith("张"))
                 .filter(s->s.length()==3)
                 .forEach(s-> System.out.print(s));//张无忌 张三丰

   //3map
    Stream<String> stream2 = Stream.of("1", "2", "3", "4");
    Stream<Integer> stream1 = stream2.map( s-> Integer.parseInt(s));
    stream1.forEach(i-> System.out.print(i));//1 2 3 4

    //4count
    Stream<String> stream = Stream.of("张", "2", "3", "4");
    Stream<String> z = stream.filter(s->s.startsWith("张"));
    System.out.println(z.count());//1

    //5limit
    Stream<String> limit = stream.limit(3);
    limit.forEach(s-> System.out.print(s));//张23

    //6skip
    Stream<String> skip = stream.skip(2);
    skip.forEach(s-> System.out.print(s));//3 4

    //7concat
    String[] arr ={"4","5","6"};
    Stream<String> arr1 = Stream.of(arr);
    Stream<String> concat = Stream.concat(arr1, stream);
    concat.forEach(s-> System.out.print(s));//456张234
}

练习:

public static void main(String[] args) {
    //第一支队伍
    ArrayList<String> one = new ArrayList<>();
    one.add("迪丽热巴");
    one.add("宋远桥");
    one.add("苏星河");
    one.add("石破天");
    one.add("石中玉");
    one.add("老子");
    one.add("庄子");
    one.add("洪七公");
    //1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
    //2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
    Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);

    //第二支队伍
    ArrayList<String> two = new ArrayList<>();
    two.add("古力娜扎");
    two.add("张无忌");
    two.add("赵丽颖");
    two.add("张三丰");
    two.add("尼古拉斯赵四");
    two.add("张天爱");
    two.add("张二狗");
    //3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
    //4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
    Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("张")).skip(2);

    //5. 将两个队伍合并为一个队伍;存储到一个新集合中。
    //6. 根据姓名创建Person对象;存储到一个新集合中。
    //7. 打印整个队伍的Person对象信息。
    Stream.concat(streamOne,streamTwo).map(name->new Person(name)).forEach(s-> System.out.println(s));
}
代码结果:
Person{name='宋远桥'}
Person{name='苏星河'}
Person{name='石破天'}
Person{name='张天爱'}
Person{name='张二狗'}

6.方法引用

双冒号::为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。

1.通过对象名引用成员方法

//函数式接口
@FunctionalInterface
public interface Printable {
    void print(String str);
}
//成员方法
public class Method {
    public void printUp(String str){
        System.out.println(str.toUpperCase());
    }
}
//测试
public class MethodTest {
    private static void print(Printable lambda){
        lambda.print("Hello");
    }
    public static void main(String[] args) {
        Method method = new Method();
		//通过对象名引用成员方法	
        print(method::printUp);//HELLO
		//lambda
        print(s->method.printUp(s));//HELLO
    }
}
  • Lambda表达式:s->method.printUp(s)
  • 方法引用:method::printUp

2.通过类名称引用静态方法

public interface Calcable {
    int abs(int num);
}
public class MethodTest02 {
    public static int method(int num,Calcable c){
        return c.abs(num);
    }

    public static void main(String[] args) {
        int number = method(-10,s-> Math.abs(s));
        System.out.println("number = " + number);

        int method = method(-10, Math::abs);
        System.out.println("method = " + method);
    }
}
  • Lambda表达式:n -> Math.abs(n)
  • 方法引用:Math::abs

3.通过super引用成员方法

public interface Greetable {
    void  greet();
}
public class Fu{
    public void hello(){
        System.out.println("我是父");
    }
}
public class ZiTest extends Fu{
    @Override
    public void hello() {
        System.out.println("我是子");
    }
    public void method(Greetable g){
        g.greet();
    }
    public void show(){
        method(()->{
            Fu fu = new Fu();
            fu.hello();
        });
        method(()-> super.hello());
        method(super::hello);
    }
    public static void main(String[] args) {
        new ZiTest().show();
    }
}
  • Lambda表达式:() -> super.sayHello()
  • 方法引用:super::sayHello

4.通过this引用成员方法

public interface Richable {
    void buy();
}
//定义方法
public void buyHouse(){
    System.out.println("买东西!");
}
//传递接口
public void marry(Richable r){
    r.buy();
}
//定义方法
public void soHouse(){
    marry(()->{
        this.buyHouse();
    });
    //使用方法
    marry(this::buyHouse);
}
public static void main(String[] args) {
    new ThisTest().soHouse();
}
  • Lambda表达式:() -> this.buyHouse()
  • 方法引用:this::buyHouse

5.类的构造器引用

public interface PersonBuilder {
    Person builder(String name);
}
//定义方法
public static void method(String name,PersonBuilder pb){
    Person builder = pb.builder(name);
    System.out.println(builder.getName());
}

public static void main(String[] args) {
    //调用
    method("张三",(String name)->{
        return new Person(name);
    });
    
    //使用方法
    method("李四",Person::new);
}
  • Lambda表达式:name -> new Person(name)
  • 方法引用:Person::new

6.数组的构造器引用

public interface ArrayBuilder06 {
    int [] arr(int length);
}
 //定义方法
    public static int[] method(int length, ArrayBuilder06 ab) {
        return ab.arr(length);
    }

    public static void main(String[] args) {
        int[] method = method(10, (len) -> {
            return new int[len];
        });
        System.out.println(method.length);
//方法
        int[] method1 = method(10, int[]::new);
        System.out.println(Arrays.toString(method1));
        System.out.println(method1.length);
    }
  • Lambda表达式:length -> new int[length]
  • 方法引用:int[]::new
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值