一、继续讲解lamdba表达式

函数的引用

lambda表达式是为了简化接口的实现的。 在lambda表达式中, 不应该出现比较复杂的逻辑。 如果在lambda表达式中出现了过于复杂的逻辑, 会对程序的可读性造成非常大的影响。 如果在lambda表达式中需要处理的逻辑比较复杂, 一般情况会单独的写一个方法。 在lambda表达式中直接引用这个方法即可。
或者, 在有些情况下, 我们需要在lambda表达式中实现的逻辑, 在另外一个地方已经写好了。 此时我们就不需要再单独写一遍, 只需要直接引用这个已经存在的方法即可。
函数引用: 引用一个已经存在的方法, 使其替代lambda表达式完成接口的实现。
复习:

  1. package com.qfedu.day16;/**
  2. * @Author laoyan
  3. * @Description TODO
  4. * @Date 2022/3/22 9:39
  5. * @Version 1.0
  6. */
  7. /**
  8. * 函数式接口
  9. */
  10. @FunctionalInterface
  11. interface Flying{
  12. void fly();
  13. }
  14. public class Demo01 {
  15. public static void main(String[] args) {
  16. Flying flying = new Flying() {
  17. @Override
  18. public void fly() {
  19. System.out.println("像风一样自由!!");
  20. }
  21. };
  22. flying.fly();
  23. // 使用的是lambda表达式简化函数式接口的写法
  24. Flying flying1 = () -> System.out.println("像风一样自由!!");
  25. flying1.fly();
  26. }
  27. }

1、静态方法引用

  • 语法:
    • 类::静态方法
  • 注意事项:
    • 在引用的方法后面, 不要添加小括号。
    • 引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的一致。 ```java package com.qfedu.day16;

/**

  • @Author laoyan
  • @Description TODO
  • @Date 2022/3/22 9:47
  • @Version 1.0 *
  • 静态方法的引用 */ @FunctionalInterface // 函数式接口,里面有一个未实现的方法 interface SingleReturnMutipleParameter{ int test(int a,int b); } public class Demo02 {

    public static void main(String[] args) {

    1. // 没有问题,但是前提条件是方法中的实现代码不多
    2. SingleReturnMutipleParameter singleReturnMutipleParameter = (a,b) -> a+b;
    3. // 如果实现的代码非常的多,我可以编写一个类,类中使用静态方法,编写完成后,直接调用即可
    4. SingleReturnMutipleParameter singleReturnMutipleParameter2 = Computer::jisuan;
    5. int num = singleReturnMutipleParameter2.test(10, 20);
    6. System.out.println(num);

    } }

/**

  • 这个类,跟接口没有任何关系,没有实现接口
  • 类中的方法更不是接口中方法的实现
  • 只要这个方法,参数个数以及参数类型以及返回值类型跟接口中的方法一致,就可以使用。 */ class Computer{

    // 是静态的 调用的时候 类名::方法名即可 public static int jisuan(int a,int b){ return a + b; } }

<a name="nJPLy"></a>
### 实战:
```java
package com.qfedu.day16;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/22 10:03
 * @Version 1.0
 */

@FunctionalInterface
interface Converter{
    //将字符串转换成整数
    Integer convert(String value);
}
public class Demo03 {

    public static void main(String[] args) {
        Converter converter = (value) -> Integer.valueOf(value);
        // 有没有一个类,类中有个静态方法,可以将字符串转换为Integer  --> Integer valueOf 方法
        Converter converter2 =  Integer::valueOf;
        System.out.println(converter2.convert("123"));
    }
}

2、非静态方法引用

  • 语法:
    • 对象::非静态方法
  • 注意事项:
    • 在引用的方法后面, 不要添加小括号。
    • 引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的一致。 ```java @FunctionalInterface // 函数式接口,里面有一个未实现的方法 interface SingleReturnMutipleParameter{ int test(int a,int b); }

class Computer{

// 是静态的  调用的时候   类名::方法名即可
public static int jisuan(int a,int b){
    return a + b;
}

// 是一个普通方法   一个普通类中的普通方法
public  int jia(int a,int b){
    return a + b;
}

}

main 方中调用: Computer computer = new Computer(); SingleReturnMutipleParameter singleReturnMutipleParameter3 = computer::jia; System.out.println(singleReturnMutipleParameter3.test(20, 30));

    // 以上对象只使用了一次,所以我们可以使用匿名对象
    SingleReturnMutipleParameter singleReturnMutipleParameter4 = new Computer()::jia;
    System.out.println(singleReturnMutipleParameter4.test(20, 30));
<a name="ObFgx"></a>
### 3、构造方法引用

- 语法 
   - 类名::new
- 注意事项 
   - 可以通过接口中的方法的参数, 区分引用不同的构造方法。
```java
package com.qfedu.day16;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/22 10:18
 * @Version 1.0
 */
class Person{
    private int age;
    private String name;

    public Person() {
        System.out.println("无参数的构造方法已经调用");
    }

    public Person( String name,int age) {
        System.out.println("有参数的构造方法已经调用");
        this.age = age;
        this.name = name;
    }
}

interface NoCanshu{
    // 如果某一个函数式接口中定义的方法, 仅仅是为了得到一个类的对象。 此时我们就可以使用构造方法的引用, 简化这个方法的实现。
    Person getPerson();
}

interface YouCanshu{
    // 如果某一个函数式接口中定义的方法, 仅仅是为了得到一个类的对象。 此时我们就可以使用构造方法的引用, 简化这个方法的实现。
    Person getPerson(String name,int age);
}
public class Demo04 {

    public static void main(String[] args) {

        // 调用无参数的构造方法
        NoCanshu noCanshu = Person::new;
        System.out.println(noCanshu);
        Person person1 = noCanshu.getPerson();
        System.out.println(person1);

        // 调用有参数的构造方法
        YouCanshu youCanshu = Person::new;
        Person person = youCanshu.getPerson("张三",19);
        System.out.println(person);
    }
}

4、对象方法的特殊引用(了解一下)

   如果在使用lambda表达式,实现某些接口的时候。 lambda表达式中包含了某一个对象, 此时方法体中, 直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。 其他的参数, 可以作为调用方法的参数。 此时, 可以对这种实现进行简化。
interface SetField {
    void set(Person person,String name);
}

SetField setField = (person,name) -> {
            person.setName(name);
};
// 以上接口完全满足对象方法的特殊引用
        SetField setField2 = Person::setName;
        setField2.set(new Person(),"张三");
package com.qfedu.day16;



interface ShowTest {
    void test(Person person);
}

interface SetField {
    void set(Person person,String name);
}

interface GetField {
    String get(Person person);
}

public class Demo05 {

    public static void main(String[] args) {

         // 使用lambda表达式是这样写
         SetField setField = (person,name) -> {
            person.setName(name);
         };
         //  通过::
         // 类名:: 方法名一般用于静态方法,目前不是
        SetField setField2 = Person::setName;
        Person p = new Person("张三", 10);
        setField2.set(p,"李四");
        System.out.println(p.getName());

        GetField getField = Person::getName;
        System.out.println(getField.get(new Person("张三", 10)));

        ShowTest showTest = (ren) -> {
            System.out.println(ren.getName()+","+ren.getAge());
        };
        ShowTest showTest1 = Person::show;
        showTest1.test(new Person("张三", 10));

    }
}

二、继续说Stream流—集合流

集合流,使用类Stream来表示,是Java8的新特性之一。使用集合流可以对集合的操作进行功能增强,流并不是集合的元素,也不是一种数据结构,不负责数据的存储操作。更像是迭代器,可以实现对集合中的元素进行单向不循环的遍历。
更像是一种对数组或者集合的一种快速的遍历手段!!!

1、如何获取流

package com.qfedu.day16_02;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/22 11:25
 * @Version 1.0
 */
public class Demo01 {

    /**
     *  获取流的方式 3种
     * @param args
     */
    public static void main(String[] args) {
        // 快速创建一个带数据的集合
        List<Integer> list =new ArrayList<>();
        Collections.addAll(list,10,20,35,19,8,22,98,78);
        // 通过集合对象获取流对象
        Stream<Integer> stream = list.stream();// 串行的流
        Stream<Integer> parallelStream = list.parallelStream();// 并行的流
        // 通过数组的类Arrays 获取流
        int[] arr = {10,20,35,19,8,22,98,78};
        IntStream intStream = Arrays.stream(arr);
    }
}

image.png

2、最终操作

forEach —> 可以循环打印流中的数据
collect —> 可以将流转换为List、Set集合

最终操作,除了这个操作得到的不再是流对象Stream,我们没有办法继续流式的操作下去之外。还有一个很重要的因素: 最终操作会关闭这个流对象本身,我们对一个已经关闭的流进行元素的处理,会有异常!
因此,我们接下来的最终操作的学习过程中切记,千万不要对一个已经关闭的流进行元素的处理。
image.png
最终操作都有哪些方法:
// collect 将流中的数据转换为集合

package com.qfedu.day16_02;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/22 11:39
 * @Version 1.0
 */
public class Demo02 {

    // 展示Stream流都有哪些最终的操作
    public static void main(String[] args) {
        List<Integer> list =new ArrayList<>();
        Collections.addAll(list,10,20,35,19,8,22,98,78);
        // 通过集合对象获取流对象
        Stream<Integer> stream = list.stream();// 串行的流
        // 获取流中的数据变为集合
        //List<Integer> list2 = stream.collect(Collectors.toList());
        //Set<Integer> set = stream.collect(Collectors.toSet());
        /*Map<Integer, Integer> map = stream.collect(Collectors.toMap(new Function<Integer, Integer>() {


            @Override
            public Integer apply(Integer integer) {
                // key
                return integer;
            }
        }, new Function<Integer, Integer>() {
            @Override
            public Integer apply(Integer integer) {
                // value
                return integer + 100;
            }
        }));
        System.out.println(map);*/


        /*Map<Integer, Integer> map2 = stream.collect(Collectors.toMap(a -> a, a -> a+100));
        System.out.println(map2);*/
        //stream.forEach(a -> System.out.println(a));
        stream.forEach(System.out::println);
    }
}
package com.qfedu.day16_02;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/22 11:39
 * @Version 1.0
 */
public class Demo03 {

    /**
     *   最终操作:
     *   collect
     *   max
     *   min
     *   forEach
     *   count
     *   reduce
     *
     *   match
     *      allMath  -- 全部元素都满足,才会返回true
     *      anyMath  -- 全部元素部分满足,就会返回true
     *      noneMath-- 全部元素都不满足,才会返回true
     *   find
     *      findFirst  -->  都是返回第一个元素
     *      findAny      
     */

    // 展示Stream流都有哪些最终的操作
    public static void main(String[] args) {
        List<Integer> list =new ArrayList<>();
        Collections.addAll(list,10,20,35,19,8,22,98,78);
        // 通过集合对象获取流对象
        Stream<Integer> stream = list.stream();// 串行的流
        // reduce 合并的意思,我给定一个流中的两个元素,你看着办
        // 得到是一个Optional对象,想要获取里面的最终的值,需要调用get方法
        /*Optional<Integer> optional = stream.reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer a, Integer b) {
                return a + b;
            }
        });
        System.out.println(optional.get());*/
        // stream.reduce((a,b) -> a + b).get();

        // 获取流中元素的个数
        // System.out.println(stream.count());

        /*Optional<Integer> max = stream.max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });*/
        /*Optional<Integer> max = stream.max((o1,o2) -> o1 - o2 ) ;
        System.out.println(max.get());*/


        /*Integer min = stream.min((o1, o2) -> o1 - o2).get();
        System.out.println(min);*/

        // 流中的元素全部满足条件才会返回true
        /*boolean match = stream.allMatch((a) -> {
            if (a > 50) {
                return true;
            }
            return false;
        });
        System.out.println(match);*/
        /*boolean match1 = stream.anyMatch(a -> {
            if (a > 50) {
                return true;
            }
            return false;
        });
        System.out.println(match1);*/

        // 流中的元素必须 全不 满足条件才会返回true
        /*boolean match2 = stream.noneMatch(a -> {
            if (a > 50) {
                return true;
            }
            return false;
        });
        System.out.println(match2);*/

        //System.out.println(stream.findFirst().get());
        //System.out.println(stream.findAny().get());
        // 都是返回第一个元素,在串行的流中是一样的,但是在并行的流中不一样。
        Stream<Integer> parallelStream = list.parallelStream();
        //System.out.println(parallelStream.findFirst().get());
        System.out.println(parallelStream.findAny().get());// 在并行的流中,findAny() 打印的数据不固定


    }
}

3、中间操作

每个中间操作的返回值都是Stream流对象。

package com.qfedu.day16_02;

import com.qfedu.day16.Person;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/22 14:40
 * @Version 1.0
 */


public class Demo04 {

    /**
     *  Stream的中间操作
     *  filter  过滤数据
     *  sorted  排序,默认是数字类型的排序,如果不是数字类型,需要指定比较器
     *  map    将里面的每一个元素,进行一个操作再返回回去
     *  distinct  去重
     *  limit   保留多少个元素
     *  skip   元素跳过多少个开始算
     *  flatMap  压扁我们的多维数组变一维
     *  mapToInt  将需要统计的字段放入进去,就可以求平均值,最大最小值
     * @param args
     */
    public static void main(String[] args) {

        List<Integer> list =new ArrayList<>();
        Collections.addAll(list,10,20,20,35,19,8,22,98,78);
        // 通过集合对象获取流对象
        Stream<Integer> stream = list.stream();// 串行的流
        stream.filter(a -> a%2 == 0 ? true:false )
                .sorted()//.map(a -> a + 100)
                .distinct()
                //  8 10 20 22 78 98
                .skip(1) // 跳过前面的多少个元素
                .limit(3)
                .forEach(System.out::println);


        String[] arr = {"Hello","World"};
        Stream<String> stream1 = Arrays.stream(arr);
        /**
         *   Hello -->  [Hell]
         *   World -->  [W, rld]
         *   flatMap -- 给我一个数组,我给它压扁
         *   [[1,2,3,4],[5,6,7,8]]  --> flatMap --> [1,2,3,4,5,6,7,8]
         */
        stream1.map( s -> s.split("o")).flatMap( _arr-> Arrays.stream(_arr)).forEach(System.out::println);


        List<Person> pList = new ArrayList<>();
        Collections.addAll(pList,
                new Person("zhangsan",19),
                new Person("lisi",22),
                new Person("wangwu",36),
                new Person("zhaoliu",78),
                new Person("laoyan",19)
        );
        Stream<Person> stream2 = pList.stream();
        // 求 平均年龄
        /**
         *  就是将元素中的那个属性变为int ,然后求其他的值
         */
        /*IntStream intStream = stream2.mapToInt(new ToIntFunction<Person>() {
            @Override
            public int applyAsInt(Person p) {
                return p.getAge();
            }
        });*/
        IntStream intStream = stream2.mapToInt(p -> p.getAge());
        // 将我们想要统计的字段放入到流中,然后通过调用方法就可以统计了
        //System.out.println(intStream.average().getAsDouble());
        System.out.println(intStream.max().getAsInt());
        //System.out.println(intStream.min().getAsInt());
    }
}

4、综合案例

package com.qfedu.day16_02;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

/**
 * @Author laoyan
 * @Description TODO
 * @Date 2022/3/22 15:21
 * @Version 1.0
 */
class Student {
    private String name;
    private int score;

    public String getName() {
        return name;
    }

    public int getScore() {
        return score;
    }

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return String.format("姓名: %s, 成绩: %d", name, score);
    }
}
public class Demo05 {
    /**
     * 需求: 一个集合中存储了了若干个Student对象, 要求查询出以下结果:
     * 1. 所有及格的学生信息
     * 2. 所有及格的学生姓名
     * 3. 所有学生的平均成绩
     * 4. 班级的前3名(按照成绩)
     * 5. 班级的3-10名(按照成绩)
     * 6. 所有不不及格的学生平均成绩
     * 7. 将及格的学生, 按照成绩降序输出所有信息
     * 8. 班级学生的总分
     */

    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        Collections.addAll(list,
                new Student("xiaoming", 89),
                new Student("xiaobai", 98),
                new Student("xiaohei", 78),
                new Student("xiaolv", 86),
                new Student("xiaowang", 59),
                new Student("xiaoxiao", 100)
        );

        Stream<Student> stream = list.stream();
        /**
         *        1. 所有及格的学生信息
         *      * 2. 所有及格的学生姓名
         *      * 3. 所有学生的平均成绩
         *      * 4. 班级的前3名(按照成绩)
         *      * 5. 班级的3-10名(按照成绩)
         *      * 6. 所有不不及格的学生平均成绩
         *      * 7. 将及格的学生, 按照成绩降序输出所有信息
         *      * 8. 班级学生的总分
         */
        /*stream.filter(p -> p.getScore()>60)
                .map(p -> p.getName())
                .forEach(System.out::println);*/
        //System.out.println(stream.mapToInt(p -> p.getScore()).average().getAsDouble());
        //stream.sorted((o1,o2) -> o2.getScore() - o1.getScore()).limit(3).forEach(System.out::println);
        //stream.sorted((o1,o2) -> o2.getScore() - o1.getScore()).skip(2).limit(7).forEach(System.out::println);
        //System.out.println(stream.filter(p -> p.getScore() < 60).mapToInt(p -> p.getScore()).average().getAsDouble());
        System.out.println(stream.mapToInt(p -> p.getScore()).sum());
    }

}

三、作业

1、作业

1. 已知一个文本内容如下:

    name:xiaoming,age:10岁,gender:male,height:172cm,weight:70kg
    age:20岁,height:177cm,name:xiaobai,weight:80kg,gender:male
    gender:female,height:176cm,weight:66kg,name:xiaolv,age:21岁


每一行是数据为一个人的信息,但是顺序是不固定的。

1.1、在桌面上创建一个GBK文件,将内容写到这个文件中。(可以使用IDEA创建GBK文件)

1.2、将桌面创建的文件用代码复制到项目下的 file\\source 路径下。

1.3、读取这个文本中的数据,将每一行的数据封装到一个Person对象中,并存入一个List集合

1.4、删除集合中所有的未成年的数据

1.5、计算所有人的平均年龄、最大年龄、最小年龄

1.6、将集合中的数据按照年龄进行降序排序,若年龄相同,按照身高降序排序

1.7、查询集合中所有的年龄在[20, 25]范围内,体重在[60, 80]范围内的数据,按照身高降序排列,截取第4名到第8名。【使用Stream流】

1.8、将集合中的数据序列化到本地

1.9、将本地序列化的文件反序列化,得到集合,并遍历输出里面的内容