Lambda表达式

  1. 为匿名内部类提供了简便写法
  2. 了解内部类的使用

    1. public static void main1(String[] args) {
    2. //在方法内部定义内部类
    3. class MyClass{
    4. public void say(){
    5. System.out.printf("我是内部类");
    6. }
    7. }
    8. //使用内部类创建对象
    9. MyClass myClass = new MyClass();
    10. myClass.say();
    11. //简便写法
    12. new MyClass().say();
    13. }
  3. 优化成匿名内部类

    1. 必须继承类,或者是实现接口

      1. public interface MyInferface1 {
      2. void run();
      3. }
    2. 在代码中使用创建对象

      1. public static void main2(String[] args) {
      2. //匿名内部类
      3. //匿名内部类,必须继承其他类或者是实现某个接口
      4. MyInferface1 myInferface1 = new MyInferface1() {
      5. @Override
      6. public void run() {
      7. System.out.println("我是匿名内部类");
      8. }
      9. };
      10. myInferface1.run();
      11. /////省略引用
      12. new MyInferface1(){
      13. @Override
      14. public void run() {
      15. System.out.println("我是匿名内部类2");
      16. }
      17. }.run();
      18. }
  4. lambda表达的优化

    1. 必须是接口,并且只有一个抽象方法

      1. public interface MyInferface1 {
      2. void run();
      3. }
    2. 必须通过引用接收创建的对象

      1. public static void main3(String[] args) {
      2. //lambda表达式对匿名内部类的优化
      3. //1,必须是接口
      4. //2, 只能有一个未实现的方法
      5. MyInferface1 myInferface1 = ()->{
      6. System.out.println("lambda表达式");
      7. };
      8. myInferface1.run();
      9. }
  5. 主要将接口当作参数传入到其他的方法中(用于模拟函数的引用传递)

    1. 定义接口

      1. public interface MyForEachInterface {
      2. void each(Object o);
      3. }
    2. 定义调用方法

      1. //实现了一个方法,可以遍历集合
      2. public static void myForEach(List list,
      3. MyForEachInterface eachInterface){
      4. for(int i=0;i<list.size();i++){
      5. //调用了传递进入来的接口的方法
      6. eachInterface.each(list.get(i));
      7. }
      8. }
    3. 使用这个myForEach方法的时候,通过lambda表达式传递方法引用

      1. public static void main(String[] args) {
      2. List<Integer> list = new ArrayList<>();
      3. for (int i=0;i<10;i++){
      4. list.add(i);
      5. }
      6. //使用lambda表达式遍历
      7. list.forEach(obj->{
      8. System.out.println("打印方式1"+obj);
      9. });
      10. myForEach(list,obj->{
      11. System.out.println("打印方式2"+obj.toString());
      12. });
      13. }

      函数接口

  6. 在java中是不能够将方法作为一个参数引用传递到其他的方法中,但是c或者是js都是可以的

  7. 在java中将只有一个方法的接口称为函数接口,然后在利用lambda表达式,让接口的传递看上去是在传递函数
  8. 为了保证函数接口的规范,jdk1.8推出一些规则

    1. 在接口上使用@FunctionalInterface,可以检测当前接口是否是函数接口

      1. @FunctionalInterface
      2. public interface MyInterface2 {
      3. void accept();
      4. }
    2. 为了方便使用,jdk提供一些自带的接口

      1. Consumer< T >:定义一个抽象方法可以传值,但是没有返回,消费接口
      2. Supplier: 有返回没有参数,只有返回结果,提供接口
      3. Function< T,R >:有参数有返回结果,功能接口
      4. Predicate< T > :可以传入参数,返回结果是boolean类型,断言接口

        方法引用

    3. 在lambda表达式上的进一步简化

      1. //使用lambda表达式输出
      2. list.forEach(integer -> {
      3. System.out.println(integer);
      4. });
      5. //使用方法引用进一步简化
      6. list.forEach(System.out::println);
      1. 可以使用方法引用的条件
        1. lambda方法体中只有一行代码
        2. 调用方法,和lambda传入的接口的方法的参数和返回保持一致

          常见几种简化形式

    4. 类::静态方法,一般用于静态方法

    5. 对象::实例方法,一般用于对象直接调用方法(传入的对象,被作为参数被省略的方法引用作为参数调用)

      1. list.forEach(s->{
      2. System.out.println(s);
      3. });
      4. list.forEach(System.out::println);
    6. 类::实例方法,这个实例方法是通过调用类代替对象调用的(调用传入对象的方法)

      1. 在类实现的一个非static方法

        1. public void say(){
        2. System.out.println("我是"+this.getName()+",今年"+this.getAge());
        3. }
      2. 在使用lambda表达式的时候使用传入的参数对象,调用say方法

        1. list.forEach(s->{
        2. s.say();
        3. });
      3. 在省略参数的时候,由于没有可以调用方法的媒介,那么可以直接使用类进行调用

list.forEach(Student::say);

  1. 类::new 对象

    1. 当返回结果是一个对象的时候,并且函数体是创建对象,那么可以使用方法引用省略
    2. 在调用的时候,构造返回的参数要和接口应用的参数保持一致 ```java public static void main(String[] args) { //匿名内部类的写法 wrap(new Supplier() { @Override public String get() {

      1. return new String();

      } }); //lambda表达式 wrap(()->{ return new String(); }); //进一步简化 wrap(()-> new String());

      //使用引用简化 wrap(String::new); }

    public static void wrap(Supplier supplier){ System.out.println(“包装开始”); String result = supplier.get(); System.out.println(“获取包装结果”);

    } ```

    简化小结

  1. 匿名内部类

    1. wrap("12", new Function<String, Object>() {
    2. @Override
    3. public Object apply(String s) {
    4. return new Short(s);
    5. }
    6. });
  2. 匿名内部类,在实现接口的情况下,并且接口之后一个方法,那么则可以简化成lambda表达式

    1. wrap("15", s -> {
    2. return new Integer(s);
    3. });
  3. lambda表达式中,如果之后一行代码,那么{}可以被省略,并且如果带返回结果的话,return也会省略

    1. wrap("15", s -> new Integer(s));
  4. 如果调用的一行代码,参数和返回与接口的抽象方法,保持一致,那么则可以使用方法引用

    1. wrap("15",Integer::new);

    Stream

  5. 流可以对集合进行筛选,快速的获取想要的结果

    1. 如果没有流,那么要遍历多次,并且还需要创建其他的集合来辅助操作

      1. public static void main2(String[] args) {
      2. List<String> list = new ArrayList<>();
      3. Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
      4. // 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
      5. //获取所有姓张的
      6. List<String> nameList = new ArrayList<>();
      7. for (int i=0;i<list.size();i++){
      8. String name = list.get(i);
      9. if(name.contains("张")){
      10. nameList.add(name);
      11. }
      12. }
      13. //在原有的基础上再次进行遍历
      14. List<String> lengthList = new ArrayList<>();
      15. for (int i=0;i<nameList.size();i++){
      16. String name = nameList.get(i);
      17. if(name.length()==3){
      18. lengthList.add(name);
      19. }
      20. }
      21. //将结果打印
      22. for (int i=0;i<lengthList.size();i++){
      23. String name = lengthList.get(i);
      24. System.out.println(name);
      25. }
      26. }
    2. 使用流简化操作

      1. public static void main(String[] args) {
      2. List<String> list = new ArrayList<>();
      3. Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
      4. // 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
      5. //获取流
      6. Stream<String> stream = list.stream();
      7. //过滤获取所有姓名张的
      8. /*
      9. stream
      10. .filter(new Predicate<String>() {//过滤方法
      11. @Override
      12. public boolean test(String o) {
      13. if(o.contains("张")){
      14. return true;
      15. }
      16. return false;
      17. }
      18. }).filter(new Predicate<String>() {//筛选
      19. @Override
      20. public boolean test(String s) {
      21. if(s.length()==3){
      22. return true;
      23. }
      24. return false;
      25. }
      26. }) .forEach(s->{//终结遍历方法
      27. System.out.println(s);
      28. });*/
      29. //过滤方法
      30. //筛选
      31. stream
      32. .filter(o -> o.contains("张"))
      33. .filter(s ->s.length()==3)
      34. .forEach(System.out::println
      35. );
      36. }
  6. 流的特点

    1. Stream 自己不会存储元素。
    2. Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
    3. Stream 操作是延迟执行的,会等到需要结果的时候才执行。
  7. 流的基本使用
    1. 从集合中获取流
    2. 对流进行筛选、切片、映射、查找、去除重复(中间操作),
      1. 可以调用多次
    3. 必须要执行一个终结操作,例如遍历,获取总数等等
      1. 只能执行一次
  8. 流的获取方式

    1. 通过Collection对象的stream()或parallelStream()方法。
    2. 通过Arrays类的stream()方法。
    3. 通过Stream接口的of()、iterate()、generate()方法。

      1. //自动无限生产数据
      2. Stream s2 = Stream.generate(new Supplier<Integer>() {
      3. @Override
      4. public Integer get() {
      5. return new Random().nextInt(100);
      6. }
      7. });
      8. s2.limit(5) //只获取5个数据
      9. .forEach(i->{
      10. System.out.println(i);
      11. });
    4. 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法。

  9. 流的中间操作的方法

    1. filter:过滤器操作
    2. limit:限制只有几个
    3. skip:跳过元素
    4. distinct:去掉重复,对象的话要重写equals方法
    5. sort:排序
      1. s2.limit(10)
      2. .sorted((o1, o2) -> o2-o1)
      3. .forEach(i->{
      4. System.out.println(i);
      5. });
  10. 流的终结操作的方法

    1. foreach:循环
    2. max:最大值
    3. reduce:可以根据自定义规则计算数据
    4. collect:将筛选之后结果,保存在新的集合中