Lambda表达式

引入函数式编程风格:

在以前调用Java方法的时候,实参必须是一个基本数据类型的值或者是一个对象,现在可以给一些引用数据类型的形参,传一段代码而不是一个对象(这段代码是一个函数的主体方法)。

Lambda表达式的好处:

可以极大地减少代码冗余,同时可读性也好过冗长的匿名内部类
并不是所有的引用数据类型都可以使用Lambda表达式,只有少部分可以使用
SAM(函数式接口)类型的形参,变量才可以赋值为Lambda表达式
函数式接口:只有一个抽象方法的接口,这个接口可以有默认方法和静态方法成员等

结论:

1.只有函数式接口才能使用Lambda表达式
2.Lambda作用就是简化代码,使代码更整洁

例如:

public class Teacher {
    private int age;
    private String name;

    public Teacher() {}

    public Teacher(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

TreeSetadd的对象必须实现Comparator接口,否则就在调用时写入,或者用Lambda写入。

public class TestLambda {
    @Test
    public void test4(){
        // TreeSet 中添加Teacher对象 Lambda表达式
        TreeSet<Teacher> teachers = new TreeSet<>((teacher1,teacher2) -> teacher1.getAge() - teacher2.getAge());
        teachers.add(new Teacher());
        teachers.add(new Teacher(18,"Y"));
        for (Teacher teacher : teachers
        ) {
            System.out.println(teacher);
        }
    }
    @Test
    public void test3(){
        // TreeSet 中添加Teacher对象
        TreeSet<Teacher> teachers = new TreeSet<>(new Comparator<Teacher>() { // 未在Teacher中实现Comparator接口 在这里传
            @Override
            public int compare(Teacher teacher, Teacher t1) {
                return t1.getAge() - teacher.getAge();
            }
        });
        teachers.add(new Teacher());
        teachers.add(new Teacher(18,"Y"));
        for (Teacher teacher : teachers
             ) {
            System.out.println(teacher);
        }
    }
    @Test
    public void test2(){
        // Lambda创建线程
        new Thread(() -> System.out.println("Lambda")).start();
    }
    @Test
    public void test1(){
        // 匿名内部类创建线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类");
            }
        }).start();
    }

}
函数式接口:

SAM (Single Abstract Method),即该接口中有且仅有一个抽象方法需要实现(当然该方法可以包含其他非抽象方法)
学习的有且仅有一个抽象方法的接口:

  • java.lang.Runnable: public void run()
  • java.lang.Comparable :public int compareTo(T t)
  • java.util.Comparator :public int compare(T t1, T t2)
  • java.lang.Iterable:public Iterator iterator()
  • java.lang.reflect.InvocationHandler:public Object invoke(Object proxy,Method method,Object[] args)
  • java.io.FileFilter:public boolean accept(File pathname)

如上接口中,都符合有且仅有一个抽象方法,但在JDK1.8之后建议在所有函数式接口添加注解@FunctionalInterface
而没有添加的说明在以后的JDK版本更新时,可能会添加抽象方法,现在用Lambda表达式没问题,但JDK更新之后不确定
Runnable、Comparator、FileFilter都是确定的函数式接口

public class TestFunctionalInterface {
    @Test
    public void test2(){
        File file = new File("D:\\IOtest\\123456\\12\\5\\123"); //是一个文件夹
        // 遍历文件夹中以.docx结尾的文件
        File[] files = file.listFiles((file1) -> file1.getName().endsWith(".docx"));//Lambda表达式:(传参) -> 结果
        for (File f : files
             ) {
            System.out.println(f);
        }
    }
    @Test
    public void test(){
        File file = new File("D:\\IOtest\\123456\\12\\5\\123"); //是一个文件夹
        // 遍历文件夹中以.docx结尾的文件
        File[] files = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                    if (file.getName().endsWith(".docx"))
                        return true;
                return false;
            }
        });
        for (File f : files
             ) {
            System.out.println(f);
        }
    }
}
@FunctionalInterface
interface MyInterface{
    void test();
}

JDK1.8新增了很多包,其中一个包:java.lang.funtion
提供了函数式接口
这些接口主要可以分为四大类:

  • 1.消费型接口(Consumer):
    它的抽象方法有参无返回值
  • 2.供给型接口(Supplier):
    无参有返回值
  • 3.判断型接口(Predicate):
    无论什么参数,输出的都是true或者false
  • 4.功能型接口(Function):
    有参有返回值
消费型接口:
  • 有参无返回值
  • 最基本的代表:Consumer : void accept(T t)
  • 例如:集合java.util.Collection系列在JDK1.8之后就增加了这样的方法:
  • default void forEach(Consumer<? super T> action)
public class TestConsumer {
    @Test
    public void test(){
        // 创建一个集合
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("你好");
        arrayList.add("世界");
        arrayList.add("java");
        arrayList.add("Lambda");
        // 遍历集合:
//        for (String s : arrayList
//             ) {
//            System.out.println(s);
//        }
        // 用forEach方法遍历 Lambda表达式
        arrayList.forEach((string) -> System.out.println(string));
    }
}
供给型接口:

无参有返回值
最基本的代表:
Supplier :T get()
举例:
StreamAPI:

  • Stream是一个数据流, java.util.stream包的Stream类型
static <T> Stream<T> generate(Supplier<T> s)
public class TestSupplier {
    @Test
    public void test(){
        // generate产生
        // Math.random():随机数
        Stream<Double> stream = Stream.generate(() -> Math.random());
        // 遍历 Lambda表达式
        stream.forEach((d) -> System.out.println(d));
    }
}
判断型接口

抽象方法:boolean test(参数)
最基本代表:
Predicate boolean test(T t)
举例:

  • java.util.Collection
    default boolean removeIf(Predicate<? super E> filter)
public class TestPredicate {
    @Test
    public void test(){
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 1; i <=10; i++) {
            arrayList.add(i);
        }
        // 移除三和三的倍数,并遍历
//        arrayList.remove(3); 太麻烦
//        arrayList.remove(6);
//        arrayList.remove(9);
        // 方法二:迭代器
//        Iterator<Integer> iterator = arrayList.iterator();
//        while (iterator.hasNext()){
//            Integer num = iterator.next();
//            if (num % 3 == 0){
//                iterator.remove();
//            }
//        }
//        for (Integer i : arrayList
//             ) {
//            System.out.println(i);
//        }
        // 方法三
        arrayList.removeIf((integer) -> integer%3 == 0);// 如果是三的倍数,即remove
        for (Integer i : arrayList
             ) {
            System.out.println(i);
        }
    }
}
lambda表达式语法:

Lambda表达式就是给函数式接口的形参或变量赋值用的,为了给这个函数式接口的抽象方法传递代码用

  • 语法格式:
    (形参列表) -> {Lambda体}
    (形参列表)就是函数式接口的抽象方法的形参列表
    {lambda体}就是函数式接口的抽象方法的方法体
  • 说明:
    1.当形参列表是空参时,那么()不能省略
    2.当形参列表是非空参时,并且数据类型是确定的或者可以推断的,那么形参的数据类型可以省略
    3.当形参列表是非空参时,并且只有一个形参,且类型也省略了,那么此时()也可以省略,如果类型没有省略,()也不能省略
    4.如果{lambda}不只有一个语句,那么{}不能省略,并且每一句都要加;
    5.如果{lambda}只有一个语句,那么{}和;都可以省略(同时省略)
    6.如果该函数式接口的抽象方法的返回值类型不是void,那么{lambda体}里要有return 返回值语句; 当{lambda}中只有一个{return 返回值;}时,{}、;、return都同时省略
public class TestLambdaGrammar {
    @Test
    public void test(){
        // 创建一个集合
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("你好");
        arrayList.add("世界");
        arrayList.add("java");
        arrayList.add("Lambda");
        arrayList.forEach(new Consumer<String>() {// 匿名内部类方式
            // 用匿名内部类方式给Consumer接口的形参赋值
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        // 用forEach方法遍历 Lambda表达式 完整版
        arrayList.forEach((String string) -> {System.out.println(string);});
    }
}
Lambda表达式练习

1.使用forEach(Consumer c)方法便利Collection系列集合
创建一个ArrayList集合,保存Teacher对象,用上述方法遍历

public class TestExer1 {
    public static void main(String[] args) {
        ArrayList<Teacher> teachers = new ArrayList<>();
        teachers.add(new Teacher(24,"Sam"));
        teachers.add(new Teacher(25,"Ann"));
        teachers.add(new Teacher(21,"Blue"));
        teachers.add(new Teacher(24,"DiDi"));
        teachers.add(new Teacher(26,"YuWei"));
        teachers.forEach(teacher -> System.out.println(teacher));
    }
}

2.使用removeIf(Predicate p) 删除元素
创建一个ArrayList集合,储存Teacher对象,删除年龄大于25的教师

public class TestExer2 {
    public static void main(String[] args) {
        ArrayList<Teacher> teachers = new ArrayList<>();
        teachers.add(new Teacher(24,"Sam"));
        teachers.add(new Teacher(25,"Ann"));
        teachers.add(new Teacher(21,"Blue"));
        teachers.add(new Teacher(24,"DiDi"));
        teachers.add(new Teacher(26,"YuWei"));
        // 删除年龄大于25的教师
        teachers.removeIf(teacher -> teacher.getAge()>25);
        // lambda遍历
        teachers.forEach(teacher -> System.out.println(teacher));
    }
}

3.(1)教师类实现Comparable接口 按年龄排序并遍历
(2)按姓名首字母排序

public class TestExer3 {
    @Test
    public void test1(){
        TreeSet<Teacher> teachers = new TreeSet<>();
        teachers.add(new Teacher(24,"Sam"));
        teachers.add(new Teacher(25,"Ann"));
        teachers.add(new Teacher(21,"Blue"));
        teachers.add(new Teacher(24,"DiDi"));
        teachers.add(new Teacher(26,"YuWei"));

        // lambda遍历
        teachers.forEach(teacher -> System.out.println(teacher));
    }
    @Test
    public void test2(){
        // 按名字首字母排列
        TreeSet<Teacher> teachers = new TreeSet<>((t1,t2) -> t1.getName().compareToIgnoreCase(t2.getName()));
        teachers.add(new Teacher(24,"Sam"));
        teachers.add(new Teacher(25,"Ann"));
        teachers.add(new Teacher(21,"Blue"));
        teachers.add(new Teacher(24,"DiDi"));
        teachers.add(new Teacher(26,"YuWei"));
        // lambda遍历
        teachers.forEach(teacher -> System.out.println(teacher));
    }

}

4.创建一个HashMap,key是你的名字,value是你对象的名字
使用Map的default void forEach()方法遍历

import java.util.HashMap;

public class TestExer4 {
    public static void main(String[] args) {
        HashMap<String,String> names = new HashMap<>();
        names.put("YYX","DLQ");
        names.forEach((yyx,dlq) -> System.out.println("我是" + yyx + "我的女朋友是" + dlq));
    }
}

感谢段段