DAY14 Lambda表达式、Stream流

学习目标

java 复制代码
- 能够掌握Lambda表达式的标准格式与省略格式(重点)
	()->{}
	(参数)->{重写接口中的抽象方法的方法体}
	(int a,int b)->{}==>省略数据类型 (a,b)->{}
	(int a)->{}==>省略数据类型 a->{}
	a->{只有一行代码}==>a->代码(return ; {} 一起省略)
- 能够通过集合、映射或数组方式获取流(重点)
	Collection集合中方法default Stream<E> stream()直接把集合转换为Stream流
		ArrayList<Integer> list = new ArrayList<>();
        Stream<Integer> stream1 = list.stream();
	Stream接口中的静态方法static <T> Stream<T> of(T... values) 把数组转换为Stream流
		Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
		String[] arr1 = {"a", "b", "c"};
        Stream<String> stream3 = Stream.of(arr1);
- 能够掌握常用的流操作(重点)
	forEach:遍历
	count:统计个数
	fiter:过滤
	limit:获取前n个
	skip:跳过前n个
	map:映射,把一种数据类型转换为另外一种数据类型
	concat:组合 把两个流组合为一个流
- 能够将流中的内容收集到集合和数组中(重点)
	List<String> list = stream.collect(Collectors.toList());
	Set<String> set = stream.collect(Collectors.toSet());
	Object[] arr = stream.toArray();

第一章 Lambda表达式(重点)

1.函数式编程思想概述

面向对象思想:

​ 做一件事情,找一个能够解决这个事情的对象,调用对象的方法来完成这件事情

函数式编程思想:

​ 重视的是结果,怎么做事情,不重视完成的过程,找谁来做

2.使用Lambda表达式简化匿名内部类(重点)

java 复制代码
package com.itheima.demo01Lambda;

public class Demo01Lambda {
    public static void main(String[] args) {
        //使用匿名内部类创建一个新的线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"-->使用匿名内部类的方式,实现多线程程序!");
            }
        }).start();

        //使用Lambda表达式简化匿名内部类创建一个新的线程
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"-->使用Lambda表达式的方式,实现多线程程序!");
        }).start();
    }
}

3.Lambda表达式的语法(重点)

Lambda表达式的语法:

​ 由一些参数,一个箭头,一些代码组成

格式:

(参数列表)->{重写抽象方法的方法体}

Lambda表达式作用:简化匿名内部类

lambda表达式使用前提:必须有接口,接口中有且只能有一个抽象方法(函数式接口)

Lambda表达式是可推导,可省略:能推导出来,Lambda表达式重写的就是接口中唯一的抽象方法

  • (参数列表):重写抽象方法的参数
  • ->:传递(固定写法):可以把参数传递到方法体中使用
  • {}:重写抽象方法的方法体
java 复制代码
/*
    Lambda表达式的基本格式
        ( )->{ }   Runnable接口-->public abstract void run();
        一些参数,一个箭头,一些代码
        ():重写接口中唯一抽象方法的参数列表,没有参数就空着,有多个参数使用逗号隔开
        ->:传递,可以把参数传递给{ }方法体使用
        { }:重写接口中唯一抽象方法的方法体
   Lambda表达式的使用前提:
        1.必须有接口
        2.接口中只能有且仅有一个抽象方法(函数式接口)     
   注意:
        Lambda表达式是可推导可省略
        Lambda表达式的目的就是重写接口中唯一的抽象方法
        接口的方法只有一个,可以推导出重写的就是接口中唯一的抽象方法,所以可以简化代码      
 */

4.使用Lambda表达式重写有参数有返回值的方法(重点)

java 复制代码
package com.itheima.demo01Lambda;

import java.util.Arrays;
import java.util.Comparator;

/*
    使用Lambda表达式重写有参数有返回值的方法(重点)
    需求:
        创建一个数组,数组的类型使用Person
        创建3个Person对象,存储到数组中
        使用Arrays数组工具类中的方法sort,根据比较器产生的规则对Person对象进行排序(年龄升序)
 */
public class Demo02Lambda {
    public static void main(String[] args) {
        //创建一个数组,数组的类型使用Person
        Person[] arr = new Person[3];

        //创建3个Person对象,存储到数组中
        arr[0] = new Person("张三",18);
        arr[1] = new Person("李四",20);
        arr[2] = new Person("王五",19);
        System.out.println("排序前数组中的元素:"+Arrays.toString(arr));

        //使用Arrays数组工具类中的方法sort,根据比较器产生的规则对Person对象进行排序(年龄升序)
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                //o1-o2:升序  o2-o1:降序
                return o1.getAge()-o2.getAge();
            }
        });
        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));

        //使用Lambda表达式简化匿名内部类   public int compare(Person o1, Person o2)
        Arrays.sort(arr,(Person o1, Person o2)->{
            return o2.getAge()-o1.getAge();
        });
        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));
    }
}
java 复制代码
package com.itheima.demo01Lambda;

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

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }
}

5.Lambda表达式简化格式

java 复制代码
package com.itheima.demo01Lambda;

import java.util.Arrays;
import java.util.Comparator;

/*
    Lambda表达式简化格式:可以使用,也可以不使用
    Lambda表达式使用前提:有接口,接口中有且仅有一个抽象方法(函数式接口)
    Lambda表达式是可推导,可以省略的
    可以推导出,Lambda表达式重写的就是接口中唯一的抽象方法
    也可以推导出方法的参数和方法有没有返回值,可以可以对参数和返回值在进行简化
    格式:
        (参数列表)->{重写抽象方法的方法体}
    简化:
        1.(参数列表):参数列表的数据类型是可以推导出来的,可以省略
            (int a)-->(a)
            (int a,String s)-->(a,s)
        2.(参数列表):参数列表中只有一个参数,()小括号也可以省略,但是参数列表没有参数,()小括号不能省略
            (int a)-->a
            ()-->不能省略-->()
        3.{重写抽象方法的方法体}:重写的方法体,如果只有一行代码(java中行是以分号隔开的)
            无论方法是否有返回值
            { }和一行代码结束的分号(;)和return关键字可以省略不写
            但是他们必须一起省略
 */
public class Demo03Lambda {
    public static void main(String[] args) {
        //使用匿名内部类创建新的线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"-->使用匿名内部类的方式,实现多线程程序!");
            }
        }).start();

        //使用Lambda表达式简化匿名内部类
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"-->使Lambda表达式的方式,实现多线程程序!");
        }).start();

        //简化Lambda表达式
        new Thread(()->System.out.println(Thread.currentThread().getName()+"-->使Lambda表达式的方式,实现多线程程序!")).start();

        System.out.println("----------------------------------------");

        //创建一个数组,数组的类型使用Person
        Person[] arr = new Person[3];

        //创建3个Person对象,存储到数组中
        arr[0] = new Person("张三",18);
        arr[1] = new Person("李四",20);
        arr[2] = new Person("王五",19);
        System.out.println("排序前数组中的元素:"+ Arrays.toString(arr));

        //使用Arrays数组工具类中的方法sort,根据比较器产生的规则对Person对象进行排序(年龄升序)
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                //o1-o2:升序  o2-o1:降序
                return o1.getAge()-o2.getAge();
            }
        });
        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));

        //使用Lambda表达式简化匿名内部类   public int compare(Person o1, Person o2)
        Arrays.sort(arr,(Person o1, Person o2)->{
            return o2.getAge()-o1.getAge();
        });
        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));

        //简化Lambda表达式
        Arrays.sort(arr,(o1,o2)->o1.getAge()-o2.getAge());

        System.out.println("排序后数组中的元素:"+Arrays.toString(arr));
    }
}

第二章 函数式接口(了解)

1.函数式接口的定义

java 复制代码
package com.itheima.demo02FunctionalInterface;

/*
    函数式接口:接口中有且仅有一个抽象方法的接口
    注意:
        1.接口中没有抽象方法不行
        2.接口中除了唯一的抽象方法,还可以包含其他的方法(默认,静态,私有)
    注解:
        @FunctionalInterface:检测接口是否为一个函数式接口
 */
@FunctionalInterface
public interface MyFunction {
    //定义抽象方法
    public abstract void show(int a);

    public static void show02(){
        System.out.println("静态方法");
    }
    
    public default void show03(){
        System.out.println("默认方法");
    }
}

2.函数式接口的作用

java 复制代码
package com.itheima.demo02FunctionalInterface;

/*
    函数式接口的作用:
        可以使用函数式接口作为方法的参数类型和返回值类型使用
        参数和返回值就可以使用Lambda表达式简化代码了
    Lambda表达式可以看成就是接口的实现类对象的一种
 */
public class Demo01MyFunction {
    public static void main(String[] args) {
        //调用method方法,方法的参数MyFunction是一个接口,需要传递接口的实现类对象,给接口变量赋值
        method(new MyFunction() {
            @Override
            public void show(int a) {
                System.out.println("匿名内部类的方式,重写show方法-->"+a);
            }
        },10);

        //调用method方法,方法的参数MyFunction是一个函数式接口,需要传递接口的实现类对象,给接口变量赋值
        //使用Lambda表达式作为接口的实现类对象
        method((int a)->{
            System.out.println("Lambda表达式的凡是,重写show方法-->"+a);
        },10);

        //简化Lambda表达式
        method(a-> System.out.println("Lambda表达式的凡是,重写show方法-->"+a),10);

        System.out.println("-------------");
        //多态  接口       实现类对象
        //MyFunction my =  a-> System.out.println(a);
        MyFunction my = getMyFunction();
        my.show(100);
    }

    /*
        定义一个方法,方法的参数类型使用函数式接口MyFunction
        MyFunction my = new MyFunction() {
            @Override
            public void show(int a) {
                System.out.println("匿名内部类的方式,重写show方法-->"+a);
            }
        };
        my.show(10);
       --------------------------------
       MyFunction my = (int a)->{
            System.out.println("Lambda表达式的凡是,重写show方法-->"+a);
        }
       my.show(10);
     */
    public static void method(MyFunction my,int i){
        my.show(i);
    }

    /*
        定义一个方法,方法的返回值类型使用函数式接口
     */
    public static MyFunction getMyFunction(){
        //方法的返回值类型是MyFunction接口,方法就需要返回一个MyFunction接口的实现类对象
        /*return new MyFunction() {
            @Override
            public void show(int a) {
                System.out.println(a);
            }
        };*/

        //方法的返回值类型是MyFunction函数式接口,所以就可以返回一个Lambda表达式作为接口的实现类对象
        /*return (int a)->{
            System.out.println(a);
        };*/

        //简化Lambda表达式
        return a-> System.out.println(a);
    }
}

3.函数式接口:Consumer

java 复制代码
package com.itheima.demo02FunctionalInterface;

import java.util.function.Consumer;

/*
    java.util.function.Consumer<T>:函数式接口
    接口中唯一的抽象方法:
        void accept​(T t) :消费一个指定泛型类型的数据
    Consumer接口的泛型使用什么类型,就可以使用accept方法消费(使用)一个什么类型的数据
    至于我们怎么使用这个数据,看心情(想怎么用就怎么用,可以计算,可以输出...)
 */
public class Demo02Consumer {
    public static void main(String[] args) {
        //调用method方法,方法的参数Consumer是一个接口,可以传递接口的匿名内部类对象,给接口变量赋值
        method(new Consumer<String>() {
            @Override
            public void accept(String s) {
                //怎么使用使用这个参数,就怎么使用(看心情)
                System.out.println(s);
            }
        },"abc");

        //调用method方法,方法的参数Consumer是一个函数式接口,可以传递Lambda表达式作为接口的实现类对象,给接口变量赋值
        method((String s)->{
            System.out.println(s);
        },"abc");

        //简化Lambda表达式
        method(s-> System.out.println(s),"abc");
    }

    /*
        定义一个方法,方法的参数
            传递Consumer接口,泛型使用String
            传递一个字符串参数
        在方法内部使用Consumer接口中的方法accept,对字符串参数进行消费
     */
    public static void method(Consumer<String> con,String s){
        con.accept(s);
    }
}

4.函数式接口:Predicate

java 复制代码
package com.itheima.demo02FunctionalInterface;

import java.util.function.Predicate;

/*
    java.util.function.Predicate<T>:函数式接口
    接口中唯一的抽象方法:
        boolean test​(T t) 用于对接口指定泛型类型的数据进行判断
    Predicate接口的泛型使用什么类型,就可以使用test方法判断数据是否满足要求
        满足要求:test方法返回true
        不满足要求;test方法返回false
 */
public class Demo03Predicate {
    public static void main(String[] args) {
        //调用method方法,方法的参数Predicate是一个接口,可以传递接口的匿名内部类对象,为接口变量赋值
        method(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                //判断字符串的长度是否大于5
                return s.length()>5;
            }
        },"aaa11111");

        //调用method方法,方法的参数Predicate是一个函数式接口,可以传递Lambda表达式作为接口的实现类对象,为接口变量赋值
        method((String s)->{
            return s.length()>5;
        },"aaa");

        //简化Lambda表达式
        method(s->s.length()>5,"afdfasfad");
    }

    /*
        定义一个方法,方法的参数
            传递Predicate接口,泛型使用String
            传递一个String类型的字符串
        在方法内部使用Predicate接口中的方法test对字符串参数进行判断
     */
    public static void method(Predicate<String> pre,String s){
        boolean b = pre.test(s);
        System.out.println(b);
    }
}

5.函数式接口:Function

java 复制代码
package com.itheima.demo02FunctionalInterface;

import java.util.function.Function;

/*
    java.util.function.Function<T,R>:函数式接口
    接口中唯一的抽象方法:
        R apply​(T t) 根据参数类型T获取类型R的接口,用于类型转换  T-->R
    例如:
        Function<String,Integer>:根据传递String类型的数据,获取Integer类型的结果  "123"  Integer.parseInt("123")
        Function<String,Person>:根据传递String类型的数据,获取Person类型的结果    "aaa"  new Person("aaa")
        ...
 */
public class Demo04Function {
    public static void main(String[] args) {
        //调用method方法,方法的参数Function是一个接口,可以传递接口的匿名内部类对象,为接口变量赋值
        method(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                //把字符串的整数,解析为一个整数返回
                return Integer.parseInt(s);
            }
        },"100");

        //调用method方法,方法的参数Function是一个函数式接口,可以传递Lambda表达式作为接口的实现类对象,为接口变量赋值
        method((String s)->{
            return Integer.parseInt(s);
        },"200");

        //简化Lambda表达式
        method(s->Integer.parseInt(s),"300");
    }

    /*
        定义一个方法,方法的参数
            使用Function接口,接口的泛型使用<String,Integer>
            传递一个String类型的参数
        在方法内部使用Function接口中的方法apply,把String类型的数据转换为Integer返回
     */
    public static void method(Function<String,Integer> fun,String s){
        Integer in = fun.apply(s);
        System.out.println(in+10);
    }
}

6.函数式接口:Supplier

java 复制代码
package com.itheima.demo02FunctionalInterface;

import java.util.Random;
import java.util.function.Supplier;

/*
    java.util.function.Supplier<T>:函数式接口
    接口中唯一的抽象方法:
        T get​() 用来获取一个接口指定泛型类型的数据
    Supplier接口使用什么泛型,就可以使用get方法获取一个什么类型的数据
 */
public class Demo05Supplier {
    public static void main(String[] args) {
        //调用method方法,方法的参数Supplier是一个接口,可以传递接口的匿名内部类对象,为接口变量赋值
        method(new Supplier<Integer>() {
            @Override
            public Integer get() {
                return 10;
            }
        });

        //调用method方法,方法的参数Supplier是一个函数式接口,可以传递Lambda表达式作为接口的实现类对象,为接口变量赋值
        method(()->{
            return 10;
        });

        //简化Lambda表达式
        method(()->10);
        method(()->new Random().nextInt(100));
    }

    /*
        定义一个方法,方法的参数
            使用Supplier接口,接口的泛型使用Integer
        在方法内部使用Supplier接口中的方法get,获取一个Integer类型的数据返回
     */
    public static void method(Supplier<Integer> sup){
        Integer in = sup.get();
        System.out.println(in);
    }
}

7.函数式接口的总结

函数式接口一般都作为方法的参数(返回值类型)

方法的参数是一个函数式接口,那么我们就可以传递lambda表达式作为接口的一种实现类对象

为方法的参数接口变量进行赋值

第三章 Stream

1.Sream流概述

我们可以把集合|数组,转换为Stream流,使用Stream流中的方法,对集合|数组进行操作

2.Stream流的基本体验

java 复制代码
package com.itheima.demo03Stream;

import java.util.ArrayList;
import java.util.List;

/*
    不使用Stream流,使用传统的方式遍历集合
 */
public class Demo01ArrayList {
    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<>();
        for (String s : list) {
            if(s.startsWith("张")){
                zhangList.add(s);
            }
        }
        System.out.println(zhangList);//[张无忌, 张强, 张三丰]

        //2.然后筛选名字中有三个字的人,把名字有三个字的人存储到一个新的集合中
        List<String> threeList = new ArrayList<>();
        for (String s : zhangList) {
            if(s.length()==3){
                threeList.add(s);
            }
        }
        System.out.println(threeList);//[张无忌, 张三丰]

        //3.对最后得到的结果threeList进行遍历数组
        for (String s : threeList) {
            System.out.println(s);
        }
    }
}
java 复制代码
package com.itheima.demo03Stream;

import java.util.ArrayList;
import java.util.List;

/*
    使用Stream流的方式操作集合:Stream流可以使用代码变的更加优雅
        把集合|数组转换为Stream流
        使用Stream流中的方法,操作数组和集合
    目的:
        简化代码
 */
public class Demo02Stream {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        //1.首先筛选出所有姓张的人,把姓张的人存储到一个新的集合中
        //2.然后筛选名字中有三个字的人,把名字有三个字的人存储到一个新的集合中
        //3.对最后得到的结果threeList进行遍历数组
        //把集合转换为Stream流
        list.stream()
                .filter(s->s.startsWith("张"))
                .filter(s->s.length()==3)
                .forEach(s-> System.out.println(s));
    }
}

3.流式思想概述

4.获取Stream流的方式(重点)

java 复制代码
package com.itheima.demo03Stream;

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

/*
    获取Stream流的方式(重点)
        1.使用Collection接口中,定义了一个方法stream,可以把集合转换为Stream流
            (JDK1.8之后)default Stream<E> stream​() 此方法只能Collection接口下的单列集合可以使用
        2.使用Stream接口中的方法of,可以把可变参数(数组)转换为Stream流
            java.util.stream.Stream<T>接口
            (JDK1.8之后)static <T> Stream<T> of​(T... values) 方法的参数是一个可变参数,也可以传递数组
 */
public class Demo03Stream {
    public static void main(String[] args) {
        show02();
    }

    /*
        把数组转换为Stream流
        注意:
            看到方法的参数是一个可变参数,就可以传递数组,因为可变参数底层就是一个数组
        (JDK1.8之后)static <T> Stream<T> of​(T... values) 方法的参数是一个可变参数,也可以传递数组    
     */
    private static void show02() {
        Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
        Stream<String> stream2 = Stream.of("a", "b", "c");
        
        String[] arr1 = {"a", "b", "c"};
        Stream<String> stream3 = Stream.of(arr1);
        //注意:数组的数据类型必须是包装类
        int[] arr2 = {1,2,3};
        Stream<int[]> stream4 = Stream.of(arr2);
        Integer[] arr3 = {1,2,3};
        Stream<Integer> stream5 = Stream.of(arr3);
    }

    /*
        把集合转换为Stream流
     */
    private static void show01() {
        ArrayList<Integer> list = new ArrayList<>();
        Stream<Integer> stream1 = list.stream();

        LinkedList<String> linked = new LinkedList<>();
        Stream<String> stream2 = linked.stream();

        HashSet<Integer> set = new HashSet<>();
        Stream<Integer> stream3 = set.stream();

        HashMap<String,String> map = new HashMap<>();
        //map.stream();//Cannot resolve method 'stream()' Map集合不能直接转换为Stream
        
        //获取Map集合中所有key
        Set<String> keySet = map.keySet();
        Stream<String> stream4 = keySet.stream();
        
        //获取Map集合中所有的value
        Collection<String> values = map.values();
        Stream<String> stream5 = values.stream();
        
        //获取Map集合中所有的Entry对象
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        Stream<Map.Entry<String, String>> stream6 = entrySet.stream();
    }
}

5.Stream的常用方法(重点)

流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

  • 终结方法 :返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用。本小节中,终结方法包括countforEach方法。
  • 非终结方法 :返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为非终结方法。)

函数拼接与终结方法

在上述介绍的各种方法中,凡是返回值仍然为Stream接口的为函数拼接方法 ,它们支持链式调用;而返回值不再为Stream接口的为终结方法,不再支持链式调用。如下表所示:

方法名 方法作用 方法种类 是否支持链式调用
count 统计个数 终结
forEach 逐一处理 终结
filter 过滤 函数拼接
limit 取用前几个 函数拼接
skip 跳过前几个 函数拼接
map 映射 函数拼接
concat 组合 函数拼接

备注:本小节之外的更多方法,请自行参考API文档。

1).forEach方法:用于遍历

java 复制代码
package com.itheima.demo03Stream;

import java.util.stream.Stream;

/*
    Stream流中常用的方法
    forEach:用于遍历Stream流中的元素
        void forEach​(Consumer<? super T> action) 对此流的每个元素执行操作。
        参数:
            Consumer<? super T> action:是一个消费型函数式接口,参数可以传递Lambda表达式
            抽象方法:
                void accept​(T t) :消费一个指定泛型类型的数据
    注意:
        forEach方法是一个终结方法,没有返回值;也不能使用链式编程继续调用Stream流中的方法了
 */
public class Demo04forEach {
    public static void main(String[] args) {
        //获取Stream流
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        //使用forEach方法遍历Stream流中的元素
        /*stream.forEach((Integer i)->{
            System.out.println(i);
        });*/

        /*
            IllegalStateException: stream has already been operated upon or closed
            Stream流只能使用一次,使用完毕会流向下一个Stream对象
            之前的Stream流对象就已经关闭了,被销毁了,所以就不能在使用了,会抛出非法状态异常
            简化lambda表达式
         */
        stream.forEach(i-> System.out.println(i));
    }
}

2).count方法:统计个数

java 复制代码
package com.itheima.demo03Stream;

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

/*
    Stream流中常用的方法
    count方法:统计个数
        long count​() 返回此流中的元素数。
    注意:
        count​方法是一个终结方法,返回值类型是long;也不能使用链式编程继续调用Stream流中的方法了
 */
public class Demo05count {
    public static void main(String[] args) {
        //获取Stream流
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        long c1 = stream.count();
        System.out.println(c1);//10

        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"aa","bb","cc","dd","ee");
        //把集合转换为Stream流
        Stream<String> stream2 = list.stream();
        System.out.println(stream2.count());//5

    }
}

3).filter方法:过滤

java 复制代码
package com.itheima.demo03Stream;

import java.util.stream.Stream;

/*
    Stream流中常用的方法
    filter方法:用于过滤Stream流中的元素
        Stream<T> filter​(Predicate<? super T> predicate)
        参数:
            Predicate<? super T> predicate:函数式接口,参数可以传递Lambda表达式
            抽象方法:
                boolean test​(T t) 用于对接口指定泛型类型的数据进行判断
   注意:
        filter方法返回值类型是Stream,是一个非终结方法,可以使用返回的Stream流对象继续调用Stream流中的其他方法(链式编程)
 */
public class Demo06filter {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream1 = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "沸羊羊",
                "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰");

        //使用filter方法过滤Stream流中的元素,只要包含羊羊的
        //Stream<String> stream2 = stream1.filter((String s) -> {
        //    return s.contains("羊羊");
        //});
        //遍历stream2流对象
        //stream2.forEach(s-> System.out.println(s));

        //链式编程
        stream1.filter(s->s.contains("羊羊")).forEach(s-> System.out.println(s));
    }
}

4).limit方法:获取前n个元素

java 复制代码
package com.itheima.demo03Stream;

import java.util.stream.Stream;

/*
    Stream流中常用的方法
    limit方法:获取前n个元素
        Stream<T> limit​(long maxSize) 返回由此流的元素组成的流,截短长度不能超过 maxSize 。
        例如
            limit(4) 获取流中的前4个元素,存储到一个新的Stream流中返回
    注意:
        1.获取的数据大于流的长度,返回流中所有元素
        2.limit方法返回值类型是Stream,是一个非终结方法,可以使用返回的Stream流对象继续调用Stream流中的其他方法(链式编程)
 */
public class Demo07limit {
    public static void main(String[] args) {
        //创建一个Stream流
        Stream<String> stream1 = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "沸羊羊",
                "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰");

        //获取Stream流中前4个元素,存储到一个新的Stream流中返回
        //Stream<String> stream2 = stream1.limit(4);
        //stream2.forEach(s-> System.out.println(s));

        //stream1.limit(4).forEach(s -> System.out.println(s));
        //stream1.limit(11).forEach(s -> System.out.println(s));//9个
        long count = stream1.limit(11).count();
        System.out.println(count);
    }
}

5).skip:跳过前n个元素

java 复制代码
package com.itheima.demo03Stream;

import java.util.stream.Stream;

/*
    Stream流中常用的方法
    skip:跳过前n个元素
        Stream<T> skip​(long n)
    注意:
        1.skip跳过的元素数量大于流中元素的个数,返回一个没有元素的空流
        2.skip​方法返回值类型是Stream,是一个非终结方法,可以使用返回的Stream流对象继续调用Stream流中的其他方法(链式编程)
 */
public class Demo08skip {
    public static void main(String[] args) {
        //获取Stream流
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        //跳过前6个元素,把剩余的元素,存储到一个新的Stream流中返回
        //Stream<Integer> stream1 = stream.skip(6);
        //stream1.forEach(s-> System.out.println(s));//7,8,9,10

        //stream.skip(6).forEach(s-> System.out.println(s));

        System.out.println(stream.skip(11).count());//0  skip跳过的元素数量大于流中元素的个数,返回一个没有元素的空流
    }
}

6).map方法:映射,类型转换

java 复制代码
package com.itheima.demo03Stream;

import java.util.stream.Stream;

/*
    Stream流中常用的方法
    map方法:映射,类型转换
        <R> Stream<R> map​(Function<? super T,? extends R> mapper)
        参数:
            Function<T,R>:函数式接口,可以传递Lambda表达式
            接口中唯一的抽象方法:
                R apply​(T t) 根据参数类型T获取类型R的接口,用于类型转换  T-->R
   注意:
     map方法返回值类型是Stream,是一个非终结方法,可以使用返回的Stream流对象继续调用Stream流中的其他方法(链式编程)
 */
public class Demo09map {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("11", "22", "33", "44");
        //使用map方法,把字符串类型的Stream流,转换为Integer类型的Stream流返回
        //Stream<Integer> stream2 = stream1.map((String s) -> {
        //    return Integer.parseInt(s);
        //});
        //遍历stream2流对象
        //stream2.forEach(s-> System.out.println(s+10));

        //链式编程
        //stream1.map(s->Integer.parseInt(s)).forEach(s-> System.out.println(s));

        Stream<String> stream3 = Stream.of("迪丽热巴", "杨幂", "柳岩");
        //使用map方法,把字符串类型的Stream流,转换为Person类型的流返回
        //Stream<Person> stream4 = stream3.map(s -> new Person(s));
        //stream4.forEach(s-> System.out.println(s));

        stream3.map(s->new Person(s)).forEach(p-> System.out.println(p));

    }
}

7).concat方法:组合

java 复制代码
package com.itheima.demo03Stream;

import java.io.Serializable;
import java.util.stream.Stream;

/*
    Stream流中常用的方法
    concat方法:组合,把两个Stream流,组合为一个新的Stream流
        static <T> Stream<T> concat​(Stream<? extends T> a, Stream<? extends T> b)
    注意:
        concat方法返回值类型是Stream,是一个非终结方法,可以使用返回的Stream流对象继续调用Stream流中的其他方法(链式编程)
 */
public class Demo10concat {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "沸羊羊",
                "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰");
        Stream<String> stream2 = Stream.of("迪丽热巴", "杨幂", "柳岩");

        //使用concat方法,把stream1和stream2组合为一个新的流
        //Stream<String> concat = Stream.concat(stream1, stream2);
        //遍历Stream流
        //concat.forEach(s-> System.out.println(s));

         Stream.concat(stream1,stream2).forEach(s-> System.out.println(s));

        //Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        //Stream<String> stream4 = Stream.of("11", "22", "33", "44");
        //Stream<Object> stream5 = Stream.concat(stream3, stream4);
        //System.out.println(stream5.count());//14
    }
}

6.综合案例(练习)

1).不使用Stream流的方式

java 复制代码
package com.itheima.demo04Stream;

import java.util.ArrayList;
import java.util.List;

/*
    Stream流综合练习题
        现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)
        依次进行以下若干操作步骤:
        1. 第一个队伍只要名字为3个字的成员姓名,把3个字的成员姓名存储到一个新的集合中
        2. 第一个队伍筛选之后只要前3个人,把前3个人存储到一个新的集合中
        3. 第二个队伍只要姓张的成员姓名,把姓张的成员姓名存储到一个新的集合中
        4. 第二个队伍筛选之后不要前2个人,跳过前2个人,把其余的人存储到一个新的集合中
        5. 将两个队伍合并为一个队伍,把两个集合组合为一个新的集合
        6. 根据姓名创建 Person 对象,把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("欧阳锋");
        one.add("蔡徐坤");

        //1. 第一个队伍只要名字为3个字的成员姓名,把3个字的成员姓名存储到一个新的集合中
        List<String> one1 = new ArrayList<>();
        for (String s : one) {
            if(s.length()==3){
                one1.add(s);
            }
        }
        System.out.println(one1);//[宋远桥, 苏星河, 洪七公, 欧阳锋, 蔡徐坤]

        //2. 第一个队伍筛选之后只要前3个人,把前3个人存储到一个新的集合中
        List<String> one2 = new ArrayList<>();
        for (int i = 0; i < 3; i++) {//i=0,1,2
            one2.add(one1.get(i));
        }
        System.out.println(one2);//[宋远桥, 苏星河, 洪七公]

        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");

        //3. 第二个队伍只要姓张的成员姓名,把姓张的成员姓名存储到一个新的集合中
        List<String> two1 = new ArrayList<>();
        for (String s : two) {
            if(s.startsWith("张")){
                two1.add(s);
            }
        }
        System.out.println(two1);//[张无忌, 张三丰, 张二狗, 张天爱, 张三]

        //4. 第二个队伍筛选之后不要前2个人,跳过前2个人,把其余的人存储到一个新的集合中
        List<String> two2 = new ArrayList<>();
        for (int i = 2; i <two1.size() ; i++) {
            two2.add(two1.get(i));
        }
        System.out.println(two2);//[张二狗, 张天爱, 张三]

        //5. 将两个队伍合并为一个队伍,把两个集合组合为一个新的集合
        List<String> all = new ArrayList<>();
        /*
            Collection集合中的方法addAll
                boolean addAll​(Collection<? extends E> c) 将指定集合中的所有元素添加到此集合
         */
        all.addAll(one2);
        all.addAll(two2);
        System.out.println(all);//[宋远桥, 苏星河, 洪七公, 张二狗, 张天爱, 张三]

        //6. 根据姓名创建 Person 对象,把Person对象存储到一个新的集合中
        List<Person> personList = new ArrayList<>();
        for (String s : all) {
            personList.add(new Person(s));
        }

        //7. 打印整个队伍的Person对象信息。
        for (Person p : personList) {
            System.out.println(p);
        }
    }
}

2).使用Stream流的方式

java 复制代码
package com.itheima.demo04Stream;

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

/*
    Stream流综合练习题:使用Stream流的方式完成
        现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)
        依次进行以下若干操作步骤:
        1. 第一个队伍只要名字为3个字的成员姓名,把3个字的成员姓名存储到一个新的集合中
        2. 第一个队伍筛选之后只要前3个人,把前3个人存储到一个新的集合中
        3. 第二个队伍只要姓张的成员姓名,把姓张的成员姓名存储到一个新的集合中
        4. 第二个队伍筛选之后不要前2个人,跳过前2个人,把其余的人存储到一个新的集合中
        5. 将两个队伍合并为一个队伍,把两个集合组合为一个新的集合
        6. 根据姓名创建 Person 对象,把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("欧阳锋");
        one.add("蔡徐坤");

        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");

        //1. 第一个队伍只要名字为3个字的成员姓名,把3个字的成员姓名存储到一个新的集合中
        //2. 第一个队伍筛选之后只要前3个人,把前3个人存储到一个新的集合中
        //把one集合转换为Stream流
        Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);

        //3. 第二个队伍只要姓张的成员姓名,把姓张的成员姓名存储到一个新的集合中
        //4. 第二个队伍筛选之后不要前2个人,跳过前2个人,把其余的人存储到一个新的集合中
        //把two集合转换为Stream流
        Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2);

        //5. 将两个队伍合并为一个队伍,把两个集合组合为一个新的集合
        //6. 根据姓名创建 Person 对象,把Person对象存储到一个新的集合中
        //7. 打印整个队伍的Person对象信息。
        Stream.concat(oneStream,twoStream).map(name->new Person(name)).forEach(s-> System.out.println(s));
    }
}

7.收集Stream结果(重点)

把Stream流转换为集合或者把Stream流转换为数组

1).把Stream流转换为集合:收集到集合中

java 复制代码
package com.itheima.demo05Stream;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/*
    把Stream流转换为集合:收集到集合中
    使用Stream流中的方法
        R collect​(Collector<?> collector) 把Stream流转换为集合
        参数:
           Collector是一个接口,需要传递接口的是实现类对象
    java.util.stream.Collectors:是一个工具类,里边提供的静态方法,可以获取Collector接口的实现类对象
        static <T> Collector<T,?,List<T>> toList​() 返回一个 Collector ,将输入元素累加到一个新的 List 。
        static <T> Collector<T,?,Set<T>> toSet​() 返回一个 Collector ,将输入元素累加到一个新的 Set 。
 */
public class Demo01StreamToCollection {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "沸羊羊",
                "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰","灰太狼");

        //把Stream流转换为List集合(有序,有索引,允许重复)
        //List<String> list = stream1.collect(Collectors.toList());
        //System.out.println(list);//[美羊羊, 喜羊羊, 懒羊羊, 沸羊羊, 暖羊羊, 慢羊羊, 灰太狼, 红太狼, 小灰灰, 灰太狼]

        //把Stream流转换为Set集合(不包含带索引的方法,不允许存储重复元素)
        Set<String> set = stream1.collect(Collectors.toSet());
        System.out.println(set);//[美羊羊, 沸羊羊, 红太狼, 灰太狼, 暖羊羊, 小灰灰, 喜羊羊, 懒羊羊, 慢羊羊]
    }
}

2).把Stream流转换为数组:收集到数组中

java 复制代码
package com.itheima.demo05Stream;

import java.util.stream.Stream;

/*
    把Stream流转换为数组:收集到数组中
    Stream流中的方法:
        Object[] toArray​() 返回一个包含此流的元素的数组。
 */
public class Demo02StreamToArray {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "沸羊羊",
                "暖羊羊", "慢羊羊", "灰太狼", "红太狼", "小灰灰","灰太狼");
        Object[] arr = stream1.toArray();
        for (Object o : arr) {
            System.out.println(o);
        }
    }
}
相关推荐
三体世界1 分钟前
C++ List的模拟实现
java·c语言·开发语言·数据结构·c++·windows·list
MobiCetus3 分钟前
【C++重点】虚函数与多态
java·开发语言·c++
神仙别闹4 分钟前
基于Java(SSM)+Mysql实现移动大厅业务办理(增删改查)
android·java·mysql
时间会给答案scidag1 小时前
maven高级
java·服务器·前端
Leo1872 小时前
parallelStream线程问题及解决方案
java·spring boot
阿黄学技术2 小时前
Redis场景问题2:缓存击穿
java·数据库·redis·缓存
写代码的橘子n4 小时前
SpringBoot项目中,controller 、 entity、mapper和service包的介绍
java·tomcat
敲键盘的小夜猫5 小时前
Redisson延迟队列实战:分布式系统中的“时间管理者“
java·redis·分布式
可爱的霸王龙5 小时前
SpringBoot整合JWT
java·后端·jwt
甜可儿6 小时前
Gateway实战入门(四)、断言-请求头以及请求权重分流等
java·spring cloud·gateway