stream

目录

Stream流的三类方法

Stream流的获取方法

Stream流中间操作方法

filter

limit,skip,concat,distinct

Stream流终结操作方法 【forEach,count】 

Stream流的收集操作Collect【终结方法】

Stream流综合练习


stream

Stream流的三类方法

  • 获取Stream流

    • 创建一条流水线,并把数据放到流水线上准备进行操作

  • 中间方法

    • 流水线上的操作

    • 一次操作完毕之后,还可以继续进行其他操作

  • 终结方法

    • 一个Stream流只能有一个终结方法【可以是forEach也开始收集方法】

    • 是流水线上的最后一个操作


Stream流的获取方法

【只能在以下四种情况才能使用Stream流】

  • Collection体系集合【单列集合】

    可以使用Collection接口中的默认方法stream()生成流, default Stream<E> stream()

  • Map体系集合【双列集合】

    • 间接的生成流                                                                                        

    • 可以先通过keySet或者entrySet获取一个Set集合,在获取Stream流 【对象名.stream】

  • 数组

        通过Arrays中的静态方法stream生成流 【Arrays.stream(数祖名)】

  • 同种数据类型的多个数据

    •    通过Stream接口的静态方法of(T... values)生成流【参数为可变参数】(Stream.of(1,2,3))

    • 比如1,2,3,4,5....     ”aaa“,"bbb","ccc"..

/**
 * Stream流的获取
 单列集合 : 集合对象.stream();
 双列集合 : 不能直接获取,需要间接获取
            集合对象.keySet().stream();
            集合对象.entrySet().stream();
 数组     :
            Arrays.stream(数组名);

 同种数据类型的多个数据:

            Stream.of(数据1,数据2,数据3......);

 */
public class MyStream2 {
    public static void main(String[] args) {
        //单列集合
        //method1();


        //双列集合
        //method2();

        //数组
        //method3();

        //同种数据类型的多个数据
        //method4();

    }

    private static void method4() {
        Stream.of(1,2,3,4,5,6,7,8).forEach(s-> System.out.println(s));
    }

    private static void method3() {
        int [] arr = {1,2,3,4,5};
        Arrays.stream(arr).forEach(s-> System.out.println(s));
    }

    private static void method2() {
        HashMap<String,Integer> hm = new HashMap<>();
        hm.put("zhangsan",23);
        hm.put("lisi",24);
        hm.put("wangwu",25);
        hm.put("zhaoliu",26);
        hm.put("qianqi",27);

        //双列集合不能直接获取Stream流
        //keySet
        //先获取到所有的键
        //再把这个Set集合中所有的键放到Stream流中
        //hm.keySet().stream().forEach(s-> System.out.println(s));


        //entrySet
        //先获取到所有的键值对对象
        //再把这个Set集合中所有的键值对对象放到Stream流中
        hm.entrySet().stream().forEach(s-> System.out.println(s));
    }

    private static void method1() {
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

//        Stream<String> stream = list.stream();
//        stream.forEach(s-> System.out.println(s));
        list.stream().forEach(s-> System.out.println(s));
    }
}

Stream流中间操作方法

中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作


filter

Stream<T> filter(Predicate predicate) 用于对流中的数据进行过滤

 参数为Predicate接口,该接口只有一个text抽象方法

boolean text(T t):对给定的参数进行判断,返回一个布尔值【给数据做一个判断,如果为true则留下,为false则不要】

【调用filter方法时会利用其参数中的实现类的方法进行条件筛选】

/**
 * Stream流的中间方法
需求:把姓张的留下
 */
public class MyStream3 {
    public static void main(String[] args) {

// Stream<T> filter​(Predicate predicate):过滤
// Predicate接口中的方法	boolean test​(T t):对给定的参数进行判断,返回一个布尔值

        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");

        /*filter方法获取流中的 每一个数据.
        而test方法中的s,就依次表示流中的每一个数据.
        我们只要在test方法中对s进行判断就可以了.
        如果判断的结果为true,则当前的数据留下
        如果判断的结果为false,则当前数据就不要.*/
       
普通写法:【使用匿名内部类写】
     list.stream().filter(
                new Predicate<String>() {
                    @Override
                    public boolean test(String s) {【参数s为流中的每一个数据,依次判断】
                       boolean result = s.startsWith("张");
                        return result;
                    }
                }
       ).forEach(s-> System.out.println(s));


使用lamdba表达式简化书写:

        【因为Predicate接口中只有一个抽象方法test
        所以我们可以使用lambda表达式来简化】
        list.stream().filter(
                (String s)->{
                    boolean result = s.startsWith("张");
                        return result;
                }
       ).forEach(s-> System.out.println(s));


进一步简化lamdba表达式:把两条语句转为一行,则return,:,大括号都可以省掉

        list.stream().filter(s ->s.startsWith("张")).forEach(s-> System.out.println(s));


    }
}

limit,skip,concat,distinct

方法名 说明
Stream<T> limit(long maxSize) 截取指定参数个数的数据【比如流中有十个数据,参数传3,截取前三个,后面的不要了】
Stream<T> skip(long n) 跳过指定参数个数的数据,【比如流中有十个数据,参数传5,前5个不要了,剩余的留下】
static <T> Stream<T> concat(Stream a, Stream b) 合并a和b两个流为一个流【把两个流的数据合并在一起】
Stream<T> distinct() 去除流中重复的元素。依赖(hashCode和equals方法)【所以如果流中的数据是自定义对象student...,一定要重写方法】

 

public class MyStream4 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");
        list.add("谢广坤");
        list.add("谢广坤");
        list.add("谢广坤");
        list.add("谢广坤");


        //method1(list);
        //method2(list);
        //method3();
        //method4(list);


    }


    private static void method4(ArrayList<String> list) {
        //        Stream<T> distinct​():去除流中重复的元素。依赖(hashCode和equals方法)
        list.stream().distinct().forEach(s-> System.out.println(s));
【只有一个谢广坤,重复的去除掉了】
    }



    private static void method3() {
        //static <T> Stream<T> concat​(Stream a, Stream b):合并a和b两个流为一个流
【此方法为静态的,是Stream接口中的静态方法,调用时直接用Stream.concat】
       

        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");

        ArrayList<String> list2 = new ArrayList<>();
        list2.add("张三丰");
        list2.add("张无忌");
        list2.add("张翠山");
        list2.add("王二麻子");
        list2.add("张良");
        list2.add("谢广坤");

一般书写:
        Stream<String> stream1 = list.stream();
        Stream<String> stream2 = list2.stream();
        Stream<String> stream3 = Stream.concat(stream1, stream2);
        stream3.forEach(s-> System.out.println(s));

简化书写:
        Stream.concat(list.stream(),list2.stream()).forEach(s-> System.out.println(s));
    }



    private static void method2(ArrayList<String> list) {
        //        Stream<T> skip​(long n):跳过指定参数个数的数据
        list.stream().skip(2).forEach(s-> System.out.println(s));
【从第三个,张翠山开始,一直到最后一个】
    }



    private static void method1(ArrayList<String> list) {
        //        Stream<T> limit​(long maxSize):截取指定参数个数的数据
        list.stream().limit(2).forEach(s-> System.out.println(s));
【张三丰,张无忌】
    }
}

Stream流终结操作方法 【forEach,count】 

  • 终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作

  • 常见方法

    方法名 说明
    void forEach(Consumer action) 对此流的每个元素执行操作【获取流中的每一个元素,并对每一个元素执行操作,一般采取的是输出操作 】
    long count() 返回此流中的元素个数

forEach方法中参数为Consumer接口

Consumer接口中的方法    只有一个抽象方法 void accept(T t):对给定的参数执行此操作

public class MyStream5 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");

        //method1(list);


        long count​():返回此流中的元素数
        long count = list.stream().count();
        System.out.println(count);


    }

   

 private static void method1(ArrayList<String> list) {

【
void forEach​(Consumer action):对此流的每个元素执行操作Consumer接口中的方法accept
void accept​(T t):对给定的参数(流中的每个数据)执行此操作

forEach方法的参数为接口,需传递一个实现类,举例采用匿名内部类实现

在forEach方法的底层,会循环获取到流中的每一个数据.
并把这些数据传递给accept方法,accept方法中的形参依次代表了流中的每一个数据
所以把对数据的处理写在accept方法中就可以了

循环调用accept方法,并把每一个数据传递给accept方法
s就依次表示了流中的每一个数据.
所以,我们只要在accept方法中,写上处理的业务逻辑就可以了.
】

         list.stream().forEach(
                new Consumer<String>() {
                    @Override
                    public void accept(String s) {
                        System.out.println(s);
                    }
                }
        );

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

lambda表达式的简化格式
是因为Consumer接口中,只有一个accept方法
        list.stream().forEach(
                (String s)->{
                    System.out.println(s);
                }
        );
        System.out.println("====================");

lambda表达式还是可以进一步简化的.
        list.stream().forEach(s->System.out.println(s));
    }
}

 案例:

 * Stream流的收集方法
 * 练习:
 * 定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10
 * 将集合中的奇数删除,只保留偶数。
 * 遍历集合得到2,4,6,8,10。

 public static void main(String[] args) {

        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }

//        list.stream().filter(【filter对每个元素进行判断】
//                (Integer i)->{  【与集合中的泛型一致】
//                    return i % 2 == 0;
//                }
//        )
        list.stream().filter(number -> number % 2 == 0).forEach(number -> System.out.print(number + "  "));

        System.out.println();
        
        for (Integer integer : list) {
            System.out.print(integer + "  ");
        }
    }

 执行结果:

2  4  6  8  10  
1  2  3  4  5  6  7  8  9  10  

 结论:在Stream流中无法直接修改集合,数组等数据源中的数据

Stream流中的数据修改的仅仅是封装在流中数据,把源数据复制一份封装到stream流对象中,对流进行过滤等操作不会影响源数据,只影响的是流中的数据


Stream流的收集操作Collect【终结方法】

  • 对数据使用Stream流的方式操作完毕后,想把流中的数据收集起来,该怎么办?

  • 可以把流中的数据收集到集合中


利用Stream流的收集方法【把流中数据存起来】

  • 方法名 说明
    R collect(Collector collector) 把结果收集到集合中

 此方法的底层只管获取到流中的数据,不管创建容器,往容器中添加数据。

创建容器并添加是额外写代码给出的。这样的容器分为三种:toList,toSet,toMap方法


工具类Collectors提供了具体的收集方式:

方法名  说明
public static <T> Collector toList() 把元素收集到List集合中
public static <T> Collector toSet() 把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中

 toList:底层创建一个list集合,并添加数据到list集合中

toSet,tooMap同理

这三个方法又是Collectors工具类的静态方法,可直接调用

收集到list,set中 :

public static void main(String[] args) {
        ArrayList<Integer> list1= new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            list1.add(i);
        }
 //filter负责过滤数据的.
        //collect只负责收集数据.
                //获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.
                创建容器并添加数据是参数中的Collectors.toList()去操作
        //Collectors.toList() : 在底层会创建一个List集合.并把所有的数据添加到List集合中.

collect(Collectors.toSet())原理:
collect方法获取流中数据,方法的形参起到把数据存入容器中并返回一个集合对象【底层是一个集合】



        List<Integer> list = list1.stream().filter(number -> number % 2 == 0)
                .collect(Collectors.toList());

        System.out.println(list);


        Set<Integer> set = list1.stream().filter(number -> number % 2 == 0)
                .collect(Collectors.toSet());
        System.out.println(set);
    }
【set是不允许重复的】

执行结果:2,4,6,8,10

练习:收集到Map集合中

 创建一个ArrayList集合,并添加以下字符串。字符串中前面是姓名,后面是年龄
  "zhangsan,23"
  "lisi,24"
  "wangwu,25"
  保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值 

 public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("zhangsan,23");
        list.add("lisi,24");
        list.add("wangwu,25");

【遍历】
        /* list.stream().filter(
                (String s)->{
                    String[] str = s.split(",");
                   int age=Integer.parseInt(str[1]);
                    return  age > 24;
                }
        ).forEach(s -> System.out.println(s));*/

        //简化
        list.stream().filter(
                 s->{
                    String[] str = s.split(",");
                    int age=Integer.parseInt(str[1]);
                    return  age > 24;
                }
        ).forEach(s -> System.out.println(s));
        

       /*    collect方法只能获取到流中剩余的每一个数据.
        在底层不能创建容器,也不能把数据添加到容器当中

        Collectors.toMap 创建一个map集合并将数据添加到集合当中

         s 依次表示流中的每一个数据

        第一个lambda表达式就是如何获取到Map中的键
        第二个lambda表达式就是如何获取Map中的值
*/

【题意解题】
    /*    Map<String, Integer> map = list.stream().filter(
                s -> {
                    String[] str = s.split(",");
                    int age = Integer.parseInt(str[1]);
                    return age > 24;
                }
        ).collect(Collectors.toMap(
                (String s) -> {
                    return s.split(",")[0];
                },
                (String s) -> {
                    return Integer.parseInt(s.split(",")[1]);
                }
                )
        );*/


        //简化lamdba
        Map<String, Integer> map = list.stream().filter(
                s -> {
                    String[] str = s.split(",");
                    int age = Integer.parseInt(str[1]);
                    return age > 24;
                }
        ).collect(Collectors.toMap(
                 s -> s.split(",")[0],
                 s -> Integer.parseInt(s.split(",")[1])
        )
        );

        System.out.println(map);
    }
}

Stream流综合练习

案例需求

现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作

  • 男演员只要名字为3个字的前三人

  • 女演员只要姓林的,并且不要第一个

  • 把过滤后的男演员姓名和女演员姓名合并到一起

  • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据

演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法

演员类: 

public class Actor {
    private String name;

 标准javaBean
get set 空参全参,tostring
}
  public static void main(String[] args) {
        //创建集合
        ArrayList<String> manList = new ArrayList<String>();
        manList.add("周润发");
        manList.add("成龙");
        manList.add("刘德华");
        manList.add("吴京");
        manList.add("周星驰");
        manList.add("李连杰");

        ArrayList<String> womanList = new ArrayList<String>();
        womanList.add("林心如");
        womanList.add("张曼玉");
        womanList.add("林青霞");
        womanList.add("柳岩");
        womanList.add("林志玲");
        womanList.add("王祖贤");
        //男演员要求
        Stream<String> stream1 = manList.stream().filter(s -> s.length() == 3).limit(3);
        //女演员要求
        Stream<String> stream2 = womanList.stream().filter(s -> s.startsWith("林")).skip(2);

        Stream.concat(stream1, stream2).forEach(name -> {
            Actor actor = new Actor(name);
            System.out.println(actor);
        });
      
    }
}

上一篇:lambda 表达式为什么不能修改局部变量


下一篇:js循环调用异步请求,实现同步