Java进阶学习8:【Lambda表达式、方法引用、Stream流】

  • A+
所属分类:Java Java进阶

01. 冗余的Lambda表达式以及方法引用初体验

/*
    Lambda表达式虽然是匿名内部类的简化写法,但是Lambda表达式有些使用也有些冗余。

    s -> System.out.println(s)
    Lambda表达式做的事情和println方法做的事情重叠了
    这个Lambda表达式做的事情是拿到参数之后,【直接】对参数进行了输出。

    如果Lambda表达式拿到参数之后, 【直接】对参数进行处理, 那么其实此时就可以想办法省略参数。
    同样的功能,可以使用方法引用去完成,方法引用就可以省略这个参数。

    方法引用是Lambda表达式的孪生兄弟, 方法引用是Lambda表达式的简化写法。

    方法引用符号是::,可以使用::去实现。
 */
public class Demo01Lambda {
    /*
        定义方法,使用Consumer当做方法参数。
        在方法中使用Consumer输出一个字符串
     */
    public static void method(Consumer<String> c) {
        //要对字符串进行操作。
        c.accept("hello");
    }

    public static void main(String[] args) {
        //调用method方法,传递Lambda表达式,lambda表达式将字符串直接输出
        method(s -> System.out.println(s));

        //方法引用初体验
        method(System.out::println);
    }
}

02. 对象引用成员方法

/*
    方法引用的常见的四种方式:
        1. 对象引用成员方法
        2. 类名引用静态方法
        3. 类的构造器引用
        4. 数组的构造器引用

    对象引用成员方法格式:
        对象名::方法名
 */
public class Demo01ObjectRef {
    /*
        定义方法, 使用函数式接口Consumer当做方法参数。
        在方法中对字符串进行操作(将字符串转成大写并输出)
     */
    public static void method(Consumer<String> c) {
        //调用accept方法,操作字符串
        c.accept("hello");
    }

    public static void main(String[] args) {
        //调用method方法
        //method(s -> System.out.println(s.toUpperCase()));

        MyClass myClass = new MyClass();
        //method(s -> myClass.printUpperCase(s));

        method(myClass::printUpperCase);//表示拿到参数后,直接使用myClass对象的printUpperCase处理这个参数
    }
}
public class MyClass {
    /*
        定义方法, 将一个字符串转成大写并输出
     */
    public void printUpperCase(String str) {
        System.out.println(str.toUpperCase());
    }
}

03. 类名引用静态方法

/*
    类名引用静态方法

    格式:
        类名::静态方法
 */
public class Demo01ClassRef {

    /*
        定义一个方法,使用函数式接口Function当做方法参数。
        在方法使用Function操作数字(求出这个数字的绝对值)
     */
    public static void method(Function<Integer, Integer> f) {
        //通过Function的apply方法操作数字
        int num = f.apply(-10);
        System.out.println(num);
    }

    public static void main(String[] args) {
        //调用method方法
        method(num -> num > 0 ? num : -num);
        //method(num -> num > 0 ? num : -num);

        //使用Math的静态方法abs求出绝对值
        //method(num -> Math.abs(num));

        method(Math::abs); //拿到参数后直接使用Math的静态方法abs处理这个参数
    }
}

04. 类的构造器引用

/*
    类的构造器引用
    类的构造器引用要在创建对象的时候使用。

    格式:
        类名::new
 */
public class Demo01ClassNew {
    /*
        定义方法, 使用自己定义的函数式接口PersonBuilder当做参数。
        在方法中使用PersonBuilder获取一个Person对象
     */
    public static void method(PersonBuilder pd) {
        //得到了Person对象。
        Person person = pd.createPerson("葫芦娃", 10);
        //输出Person
        System.out.println(person);
    }

    public static void main(String[] args) {
        //调用method方法,传递Lambda表达式
        //method((name, age) -> new Person(name, age));

        method(Person::new); //表示拿到参数后,直接根据这些参数使用构造方法创建对象。
    }
}
@FunctionalInterface
public interface PersonBuilder {
    //定义方法,参数传递姓名和年龄, 返回的结果是一个Person。
    Person createPerson(String name, int age);
}
public class Person {
    private String name;
    private int age;

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

    public Person() {
    }

    public Person(String name, int age) {

        this.name = name;
        this.age = age;
    }

    public String getName() {

        return name;
    }

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

    public int getAge() {
        return age;
    }

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

05. 数组的构造器引用

/*
    数组的构造器引用
    数组的构造器引用在创建数组的时候使用。

    格式:
        数据类型[]::new
 */
public class Demo01ArrayNew {
    /*
        定义方法,使用函数式接口ArrayBuilder当做方法参数
        在方法中使用ArrayBuilder获取一个数组
     */
    public static void method(ArrayBuilder arrayBuilder) {
        //调用createArray得到一个数组
        int[] arr = arrayBuilder.createBuilder(10);
        //输出数组
        System.out.println(Arrays.toString(arr));
    }

    public static void main(String[] args) {
        //调用method方法,传递一个lambda表达式
        //method(len ->  new int[len]);

        method(int[]::new);//表示拿到参数之后,直接根据这个参数构建一个int数组
    }
}

@FunctionalInterface
public interface ArrayBuilder {
    //定义方法,参数是一个数字表示数组的长度, 返回的结果是一个int数组
    int[] createBuilder(int len);
}

06. 传统for循环遍历集合的弊端以及Stream流初体验

/*
    定义集合,并添加元素,然后执行下面的操作
        1. 首先筛选所有姓张的人;
        2. 然后筛选名字有三个字的人;
        3. 最后进行对结果进行打印输出。
 */
public class Demo01PrintList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        //1. 首先筛选所有姓张的人;
        List<String> zhangList = new ArrayList<>();
        //遍历原来的集合,拿到里面的每一个元素,判断是否以张开头,如果是就表示姓张,那么就添加到           zhangList
        for (String s : list) {
            if(s.startsWith("张")) {
                zhangList.add(s);
            }
        }
        //2. 然后筛选名字有三个字的人;
        List<String> threeList = new ArrayList<>();
        //遍历之前筛选后的集合,拿到里面的每一个元素,判断是否是三个字,如果是,就添加到threeList
        for (String s : zhangList) {
            if(s.length() == 3) {
                threeList.add(s);
            }
        }
        //3. 最后进行对结果进行逐一打印输出。
        for (String s : threeList) {
            System.out.println(s);
        }
        System.out.println("======================");
        //Stream流初体验
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() ==3).forEach(System.out::println);
    }
}

07. Stream流的介绍

参考 Java基础学习16:【方法引用、Stream流】的图

08. Collection集合获取流

/*
    Stream表示流。

    获取流的三种方式
        1. 通过Collection(单列)集合获取流
        2. 通过Map(双列)集合获取流
        3. 通过数组获取流

    通过Collection(单列)集合获取流的方式
    如果要使用Collection单列集合获取流,可以使用Collection中的方法stream去完成。
    获取流的方法:
        Stream<E> stream(): 获取一个流对象
 */
public class Demo01Stream {
    public static void main(String[] args) {
        //创建List集合
        List<String> list = new ArrayList<>();
        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        //获取这个集合对应的流
        Stream<String> stream = list.stream();
        //将stream流转成数组,借助数组的工具类打印。
        System.out.println(Arrays.toString(stream.toArray()));
    }
}

09. 通过Map集合获取流

/*
    通过Map集合获取流(了解),Map集合不能直接获取流对象, 只能间接获取, 有三种间接获取方式。
        1. 先获取Map集合中的所有的key, 然后获取所有key的stream流
        2. 先获取Map集合中的所有的value,然后获取所有value的stream流
        3. 先获取Map集合中的所有的Entry(键值对), 获取所有entry的stream流。
 */
public class Demo02Stream {
    public static void main(String[] args) {
        //定义Map集合
        Map<String, String> map = new HashMap<>();
        //添加元素
        map.put("it001", "jack");
        map.put("it002", "rose");
        map.put("it003", "tony");
        //先获取Map集合中的所有的key, 然后获取所有key的stream流
        Set<String> keys = map.keySet();
        Stream<String> keyStream = keys.stream();
        System.out.println(Arrays.toString(keyStream.toArray()));

        //先获取Map集合中的所有的value,然后获取所有value的stream流
        Collection<String> values = map.values();
        Stream<String> valueStream = values.stream();
        System.out.println(Arrays.toString(valueStream.toArray()));

        //先获取Map集合中的所有的Entry(键值对), 获取所有entry的stream流。
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> entryStream = entries.stream();
        System.out.println(Arrays.toString(entryStream.toArray()));
    }
}

10. 通过数组获取Stream流

/*
    通过数组获取流
    方式一:使用Stream的静态方法of完成(推荐, 因为参数不仅可以传递数组, 也可以传递任意个数据)
        static <T> Stream<T> of(T... values): 根据一个数组获取一个stream流

    方式二: 使用Arrays的静态方法stream完成
        static <T> Stream<T> stream(T[] array) : 根据一个数组获取对应的stream流

 */
public class Demo03Stream {
    public static void main(String[] args) {
        //定义数组
        String[] strArr = {"aa", "bb", "cc"};
        //方式一:使用Stream的静态方法of完成
        //Stream<String> stream = Stream.of(strArr);
        Stream<String> stream = Stream.of("aa", "zz", "xx");
        //输出stream中的内容
        System.out.println(Arrays.toString(stream.toArray())); //[aa, bb, cc]

        //方式二: 使用Arrays的静态方法stream完成
        Stream<String> stream2 = Arrays.stream(strArr);
        System.out.println(Arrays.toString(stream2.toArray()));
    }
}

11. Stream中的forEach方法

/*
    在Stream中,有一个方法叫做foreach,可以对流中的元素进行逐一处理。
        void forEach(Consumer action): 该方法用来对流中元素进行逐一处理, 参数表示如何进行处理。

    回忆Consumer表示消费者,里面有一个抽象方法accept,可以去消费一个数据。
        void accept(T t): 用来消费一个数据(使用一个数据)

    因为forEach方法参数是函数式接口Consumer,所以我们可以传递Lambda表达式,表示的就是如何处理。
 */
public class Demo01Foreach {
    public static void main(String[] args) {
        //通过Stream的of方法获取一个Stream流
        Stream<String> stream = Stream.of("aaaaa", "bb", "cccccc", "dd", "eeeee");
        //使用forEach方法逐一处理里面的数据, 逐一打印
        //Lambda表达式中的s表示的是集合中的每一个元素。
        //stream.forEach(s -> System.out.println(s));
        stream.forEach(System.out::println);
    }
}

12. Stream中的filter方法

/*
    在Stream中,有一个方法叫做Filter,可以对流中的内容进行筛选(过滤)
        Stream filter(Predicate predicate): 用来对流中的元素进行过滤。 参数Predicate表示过滤规则。

    回忆Predicate是一个函数式接口, 这个函数式接口可以用来验证一个数据是否符合要求
    抽象方法:
        boolean test(T t)

    filter参数是一个Predicate函数式接口,所以我们可以传递一个Lambda表达式, 这个Lambda表达式真正表示的
    是Predicate中抽象方法test的内容(重写后), 我们要在Lambda表达式中定义筛选规则,如果Lambda表达式方法体
    的结果是true就表示留下
 */
public class Demo02Filter {
    public static void main(String[] args) {
        //获取一个Stream流
        Stream<String> stream = Stream.of("aaaaa", "bb", "cccccc", "dd", "eeeee");
        //对流中的内容进行筛选, 筛选出(留下)长度大于3的字符串
        Stream<String> newStream = stream.filter(s -> s.length() > 3);
        //使用forEach逐一处理,打印流中的每一个元素
        newStream.forEach(s -> System.out.println(s));
    }
}

13. Stream中的count方法

/*
    在Stream中,还有一个方法,可以获取流的大小(流中元素的个数)
        long count(): 返回流中元素的个数
 */
public class Demo03Count {
    public static void main(String[] args) {
        //获取一个流
        Stream<String> stream = Stream.of("aa", "bb", "cc", "dd");
        //调用count方法,获取流中元素的个数
        long count = stream.count();

        System.out.println(count);
    }
}

14. Stream中的limit方法

/*
    在Stream中有一个方法,可以获取流中的前n个元素
        Stream<T> limit(long n): 获取流中的前n个元素,并放入到一个新的流中返回。
 */
public class Demo04Limit {
    public static void main(String[] args) {
        //获取一个Stream流
        Stream<String> stream = Stream.of("aa", "bb", "cc", "dd", "ee");
        //Stream<T> limit(long n): 获取流中的前n个元素
        Stream<String> newStream = stream.limit(3);
        //使用forEach进行逐一处理
        newStream.forEach(s -> System.out.println(s));
    }
}

15. Stream中的skip方法

/*
    在Stream中,有一个方法可以跳过流中的前n个元素,获取剩下的元素。
        Stream<T> skip(long n): 跳过前n个元素,获取剩下的元素并放入到一个新的流中返回。
 */
public class Demo05Skip {
    public static void main(String[] args) {
        //获取流
        Stream<String> stream = Stream.of("aa", "bb", "cc", "dd", "ee");
        //Stream<T> skip(long n): 跳过前n个元素
        Stream<String> newStream = stream.skip(3);
        //打印新的流中的每一个元素
        newStream.forEach(System.out::println);
    }
}

16. Stream中的concat方法

/*
    在Stream中,有一个静态方法,可以对两个流进行合并。
        static Stream concat(Stream a, Stream b):对两个流进行合并, 返回合并后新的流。
 */
public class Demo06Concat {
    public static void main(String[] args) {
        //获取两个流
        Stream<String> streamOne = Stream.of("aa", "bb");
        Stream<String> streamTwo = Stream.of("cc", "dd");

        //对这两个流进行合并
        Stream<String> stream = Stream.concat(streamOne, streamTwo);
        //输出合并后的流中的每一个元素
        stream.forEach(System.out::println);
    }
}

17. Stream中的map方法

/*
    在Stream中,有一个方法,可以将流中的元素映射到一个新的流中。
    映射表示的意思就是将原来流中的每一个元素进行处理,然后保存到一个新的流中。

    原来的流:"10", "20", "30"
    映射后流: 10,   20,   30

    Stream中用于映射的方法:
        Stream map(Function mapper): 可以将原来流中的内容映射到新的流中。 参数Function表示映射规则

    回忆Function是一个函数式接口, 可以接收一个数据并产出一个结果。
    抽象方法:
        R apply(T t): 接收一个数据,产出结果。

    因为参数Function是一个函数式接口,所以我们可以传递Lambda表达式, 这个Lambda表达式表示的是Function中的
    抽象方法apply的内容。 我们要在Lambda表达式中定义映射规则。
 */
public class Demo07Map {
    public static void main(String[] args) {
        //获取一个Stream流
        Stream<String> stream = Stream.of("10", "20", "30");
        //将原来stream流中的每一个元素变成数字,然后放入到新的流中(将原来流中的元素映射到新的流)
        //Stream<Integer> newStream = stream.map(s -> Integer.parseInt(s));
        Stream<Integer> newStream = stream.map(Integer::parseInt);

        //输出新的流中的内容
        newStream.forEach(System.out::println);
    }
}

19. Stream流的注意事项

/*
    注意:
        1. Stream流中的非终结方法返回的都是Stream对象, 返回的Stream是一个新的Stream
        2. Stream流只能一次性使用,不能多次操作。
 */
public class Demo01Attention {
    public static void main(String[] args) {
        //获取一个Stream流
        Stream<String> stream = Stream.of("aa", "bb", "cc");
        Stream<String> newStream = stream.limit(1);
        //对比stream和newStream的地址值是否一样
        //System.out.println(stream == newStream);
        stream.count();
    }
}

20. 使用传统方式操作集合的练习

/*
    现在有两个ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以
    下若干操作步骤:
    1. 第一个队伍只要名字为3个字的成员姓名;
    2. 第一个队伍筛选之后只要前3个人;
    3. 第二个队伍只要姓张的成员姓名;
    4. 第二个队伍筛选之后不要前2个人;
    5. 将两个队伍合并为一个队伍;
    6. 根据姓名创建Person 对象;
    7. 打印整个队伍的Person对象信息。
 */
public class Demo01StreamTest {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
        one.add("欧阳锋");
        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");
        //1. 第一个队伍只要名字为3个字的成员姓名;
        List<String> oneA = new ArrayList<>();
        //遍历one,拿到里面的每一个元素进行判断,如果长度是3,那么就添加到oneA中
        for (String s : one) {
            if(s.length() == 3) {
                oneA.add(s);
            }
        }
        //2. 第一个队伍筛选之后只要前3个人;
        List<String> oneB = new ArrayList<>();
        //遍历oneA集合, 只遍历三次,获取元素添加到oneB中。
        for(int i = 0; i < 3; i++) {//0 1 2
            String s = oneA.get(i);//拿到遍历到的元素
            oneB.add(s);
        }
        //3. 第二个队伍只要姓张的成员姓名;
        List<String> twoA = new ArrayList<>();
        //遍历two,按到里面的每一个元素进行判断,如果是以张开头,那么就添加到twoA中
        for (String s : two) {
            if(s.startsWith("张")) {
                twoA.add(s);
            }
        }
        //4. 第二个队伍筛选之后不要前2个人;
        List<String> twoB = new ArrayList<>();
        //遍历twoA, 跳过前两次循环, 把遍历到的元素添加到twoB中
        for(int i = 2; i < twoA.size(); i++) {
            //拿到遍历到的元素
            String s = twoA.get(i);
            //添加
            twoB.add(s);
        }
        //5. 将两个队伍合并为一个队伍;将twoB和oneB合并到一个集合中
        List<String> totalList = new ArrayList<>();
        totalList.addAll(oneB);//将oneB中的所有的元素添加到totalList中
        totalList.addAll(twoB);
        //6. 根据姓名创建Person 对象;
        //定义集合,保存Person, 根据原来集合中的姓名创建Person对象, 并将Person对象保存到新的集合        中。
        List<Person> personList = new ArrayList<>();
        //遍历totalList,拿到里面的每一个姓名,根据姓名创建一个Person对象并添加到新的集合
        for (String name : totalList) {
            personList.add(new Person(name));
        }
        //7. 打印整个队伍的Person对象信息。
        for (Person p : personList) {
            System.out.println(p);
        }
    }
}

public class Person {
    private String name;

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

    public Person(String name) {
        this.name = name;
    }

    public Person() {

    }

    public String getName() {

        return name;
    }

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

21. Stream流操作集合的练习

/*
    现在有两个ArrayList 集合存储队伍当中的多个成员姓名,要求使用Stream流实现
    下若干操作步骤:
    1. 第一个队伍只要名字为3个字的成员姓名;
    2. 第一个队伍筛选之后只要前3个人;
    3. 第二个队伍只要姓张的成员姓名;
    4. 第二个队伍筛选之后不要前2个人;
    5. 将两个队伍合并为一个队伍;
    6. 根据姓名创建Person 对象;
    7. 打印整个队伍的Person对象信息。
 */
public class Demo02StreamTest {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
        one.add("欧阳锋");
        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");
        //1. 第一个队伍只要名字为3个字的成员姓名;
        //2. 第一个队伍筛选之后只要前3个人;
        Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);
        //3. 第二个队伍只要姓张的成员姓名;
        //4. 第二个队伍筛选之后不要前2个人;
        Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("张")).skip(2);
        //5. 将两个队伍合并为一个队伍;
        //6. 根据姓名创建Person 对象;
        //   根据原来Stream流中的每一个姓名创建一个Person对象,并放入到新的流中。 就是将每一个姓名变成Person对象映射到新的流中
        //7. 打印整个队伍的Person对象信息。
        Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println);
    }
}

22. 将Stream中的元素收集到集合

/*
    将Stream流中的数据收集出来,流中的数据可以收集到集合中,也可以收集到数组中(可以将流转成集合,也可以将流转成数组)

    如何将流中的元素收集到集合中?
    我们可以使用Stream中的方法collect去完成

    Stream中用于收集的方法:
        R collect(Collector collector):可以将流中的数据收集到集合。 参数Collector表示收集到哪种集合。Collector是一个接口,如果要传递需要传递Collector实现类对象, 这个实现类对象不能由我们自己去new,要通过Collectors工具类的静态方法进行获取, 使用不同方法获取的Collector,表示收集到了不同的集合中。
     static Collector toList():如果使用toList获取的Collector对象,表示将数据收集到List集合中。
     static Collector toSet()::如果使用toSet获取的Collector对象,表示将数据收集到Set集合中。
 */
public class Demo01StreamData {
    public static void main(String[] args) {
        //获取一个Stream流
        Stream<String> stream = Stream.of("aa", "bb", "cc", "dd");
        //将stream流中的数据收集到List集合
        //List<String> list = stream.collect(Collectors.toList());
        //System.out.println(list);

        //将stream流中的数据收集到Set集合
        Set<String> set = stream.collect(Collectors.toSet());
        System.out.println(set);
    }
}

23. 将流中的内容收集到数组中

/*
    将流中的元素收集到数组中。

    我们可以使用Stream中的toArray方法进行收集
        Object[] toArray(): 将流中的数据收集到数组, 返回Object[]
 */
public class Demo02StreamData {
    public static void main(String[] args) {
        //获取流
        Stream<String> stream = Stream.of("aa", "bb", "cc", "dd");
        //调用toArray方法,将流中的元素收集到数组中
        Object[] objs = stream.toArray();
        //打印数组
        System.out.println(Arrays.toString(objs));
    }
}
  • 资源分享QQ群
  • weinxin
  • 官方微信公众号
  • weinxin
沙海
一个Java基础入门的教程视频
Linux服务器网站环境安装
C语言速查手册
C语言项目源码分享

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: