day09【线程状态、等待与唤醒、Lambda表达式、Stream流】

今日内容

  • 多线程

    • 线程的状态------>必须掌握
    • 等待唤醒机制------>必须掌握
  • Lambda表达式
    • Lambda表达式的使用场景------>建议掌握
    • Lambda表达式的格式(标准\省略)------>建议掌握
  • Stream流
    • 流式思想的概述
    • 使用Stream流------>建议掌握
      • 获取流–>操作流–>收集结果

第一章 线程状态

1.1 线程状态

线程状态概述

线程由生到死的完整过程:技术素养和面试的要求。

线程从创建到销毁的过程称为线程的生命周期,在线程的生命周期内一共有六种状态:

线程状态 导致状态发生条件
NEW(新建) 线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程对象,没有线程特征。创建线程对象时
Runnable(可运行) 调用了 start() 方法,此时线程可能正在执行,也可能没有,这取决于操作系统的调度。调用start方法时
Blocked(锁阻塞) 当线程试图获取锁对象,而该锁对象被其他的线程持有,则该线程进入锁阻塞状态;当该线程获取到锁对象时,该线程将变成可运行状态。等待锁对象时
Waiting(无限等待) 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。调用wait()方法时
Timed Waiting(计时等待) 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。调用sleep()方法时
Teminated(被终止) 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。run方法执行结束时

线程状态的切换

我们不需要去研究这几种状态的实现原理,我们只需知道在做线程操作中存在这样的状态。那我们怎么去理解这几个状态呢,新建与被终止还是很容易理解的,我们就研究一下线程从Runnable(可运行)状态与非运行状态之间的转换问题。

1.2 计时等待和无限等待

  • 计时等待: 调用线程类的 sleep() 方法可使当前线程进入睡眠状态,当睡觉时间达到时线程会被自动唤醒。

    • public static void sleep(long time) 让当前线程进入到睡眠状态,到毫秒后自动醒来继续执行

      public class Test {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 100; i++) {System.out.println("i的值:"+i);// 打印一次暂停3秒Thread.sleep(3000);}}
      }
      
  • 无限等待

    • Object类的方法:

      • public void wait() : 让当前线程进入到等待状态 此方法必须锁对象调用.
  • public void notify() : 唤醒当前锁对象上等待状态的线程 此方法必须锁对象调用.

    • public void notifyAll() : 唤醒当前锁对象上所有等待状态的线程 此方法必须锁对象调用.

    • 案例1: 无限等待线程

      public class Test1 {// 锁对象public static Object obj = new Object();public static void main(String[] args) {// 创建并启动线程--->进入无限等待new Thread(new Runnable() {@Overridepublic void run() {System.out.println("无限等待线程: 准备进入无限等待...");synchronized (obj) {try {obj.wait();// 进入无限等待} catch (InterruptedException e) {e.printStackTrace();}System.out.println("无限等待线程:被其他线程唤醒,并获取到锁对象,继续执行...");}}}).start();}
      }
      
    • 案例2: 等待和唤醒案例

      public class Test2 {// 锁对象public static Object obj = new Object();public static void main(String[] args) throws InterruptedException {// 创建并启动线程--->进入无限等待new Thread(new Runnable() {@Overridepublic void run() {System.out.println("无限等待线程: 准备进入无限等待...");synchronized (obj) {// 加锁try {obj.wait();// 进入无限等待} catch (InterruptedException e) {e.printStackTrace();}System.out.println("无限等待线程:被其他线程唤醒,并获取到锁对象,继续执行...");}// 释放锁}}).start();// 为了让无限等待线程先执行,开启唤醒线程之前睡眠一下Thread.sleep(100);// 创建并启动线程--->唤醒等待线程new Thread(new Runnable() {@Overridepublic void run() {System.out.println("唤醒线程:准备唤醒无限等待线程");synchronized (obj){obj.notify();}}}).start();/*分析程序:1.程序进入无限等待状态会释放锁,释放cpu,并且不会再去抢占2.无限等待线程被唤醒,拿到锁对象之后,会从进入无限等待的位置继续往下执行*/}
      }

1.3 等待唤醒机制

什么是等待唤醒机制

  • 概述: 使用等待和唤醒实现多条线程之间有规律的执行
  • 例如: 子线程打印i循环,主线程打印j循环
  • 不使用等待唤醒机制: 结果是主线程和子线程随机交替打印输出----->没有规律
  • 使用等待唤醒机制: 结果就要有规律的打印输出
    • 打印1次i循环,然后打印1次j循环…依次循环打印输出…---->有规律
    • 如何实现:
      • 子线程打印1次i循环,然后唤醒主线程来执行, 就进入无限等待
      • 主线程打印1次j循环,然后唤醒子线程来执行,就进入无限等待
      • 子线程打印1次i循环,然后唤醒主线程来执行,就进入无限等待
      • 主线程打印1次j循环,然后唤醒子线程来执行,就进入无限等待

如何实现等待唤醒机制:

  • 1.使用锁对象调用wait()方法进入无限等待

  • 2.使用锁对象调用notify()方法唤醒线程

  • 3.调用wait(),notify()方法的锁对象要一致

  • 案例: 主线程和子线程有规律的交替打印输出

    public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {synchronized (Test.lock) {if (Test.flag == false){// 无限等待try {Test.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}if (Test.flag == true) {System.out.println("子线程i的值是:" + i);Test.lock.notify();Test.flag = false;}}}}
    }public class Test {// 锁对象public static Object lock = new Object();// 开关变量---旗帜变量public static boolean flag = false;// true: 子线程执行  false: 主线程执行public static void main(String[] args) {// 需求: 主线程和子线程有规律的交替打印输出// 创建并启动子线程new MyThread().start();// 主线程的任务for (int j = 0; j < 100; j++) {synchronized (lock) {if (flag == true){// 无限等待try {Test.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}if (flag == false) {System.out.println("主线程j的值是:" + j);lock.notify();flag = true;}}}// 结果:// j-i-j-i-j-i-j....}
    }

分析等待唤醒机制程序的执行

  • 1.不管是否使用等待唤醒机制,线程的调度都是抢占式
  • 2.线程进入无限等待,线程就会释放锁,cpu,也不会再去争夺
  • 3.唤醒其他线程,当前唤醒线程是不会释放锁,cpu的
  • 4.无限等待线程被唤醒,拿到锁对象后,会从进入无限等待的位置继续往下执行

1.4 等待唤醒案例

需求

  • 等待唤醒机制其实就是经典的“生产者与消费者”的问题。

  • 就拿生产包子消费包子来说等待唤醒机制如何有效利用资源:

分析

包子铺线程生产包子,生产完了,包子就有了,唤醒吃货线程来吃包子,然后包子铺线程进入无限等待;
吃货线程吃包子,吃完了,包子就没有了,唤醒包子铺线程来生产包子,然后吃货线程进入无限等待;
包子铺线程生产包子,生产完了,包子就有了,唤醒吃货线程来吃包子,然后包子铺线程进入无限等待;
吃货线程吃包子,吃完了,包子就没有了,唤醒包子铺线程来生产包子,然后吃货线程进入无限等待;
.....
包子类:状态--->false:没有包子,ture:有包子馅儿
包子铺线程:包子有了,进入无限等待包子没有了,执行代码(生产包子),唤醒其他线程,修改旗帜变量
吃货线程:包子没有了,进入无限等待包子有了,执行代码(吃包子),唤醒其他线程,修改旗帜变量

实现

  • 包子类

    public class BaoZi {public String xian;public boolean flag;// 默认值为false
    }
  • 包子铺线程

    public class BaoZiPu extends Thread {BaoZi bz;public BaoZiPu(BaoZi bz) {this.bz = bz;}@Overridepublic void run() {while (true) {synchronized (bz) {// 包子有了,进入无限等待if (bz.flag == true){try {bz.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 包子没有了,执行代码(生产包子),唤醒其他线程,修改旗帜变量if (bz.flag == false){System.out.println("包子铺线程:开始做包子...");bz.xian = "韭菜馅儿";bz.flag = true;bz.notify();System.out.println("包子铺线程:包子做好了,吃货快来吃包子...");}}}}
    }
  • 吃货线程

    public class ChiHuo extends Thread {BaoZi bz;public ChiHuo(BaoZi bz) {this.bz = bz;}@Overridepublic void run() {while (true) {synchronized (bz) {// 包子没有了,进入无限等待if (bz.flag == false){try {bz.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 包子有了,执行代码(吃包子),唤醒其他线程,修改旗帜变量if (bz.flag == true){System.out.println("吃货线程:开始吃包子,包子的馅儿:"+bz.xian);bz.flag = false;bz.notify();System.out.println("吃货线程:包子吃完了,包子铺快来做包子========");}}}}
    }
  • 测试类

    public class Test {public static void main(String[] args) {// 创建包子对象BaoZi bz = new BaoZi();// xian: null,flag: false// 创建并启动包子铺线程new BaoZiPu(bz).start();// 创建并启动吃货线程new ChiHuo(bz).start();}
    }

第二章 Lambda表达式

2.1 函数式编程思想概述

面向对象编程思想

面向对象强调的是对象 , “必须通过对象的形式来做事情”,相对来讲比较复杂,有时候我们只是为了做某件事情而不得不创建一个对象 , 例如线程执行任务,我们不得不创建一个实现Runnable接口对象,但我们真正希望的是将run方法中的代码传递给线程对象执行

函数编程思想

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。例如线程执行任务 , 使用函数式思想 , 我们就可以通过传递一段代码给线程对象执行,而不需要创建任务对象

2.2 Lambda表达式的体验

  • 实现Runnable接口的方式创建线程执行任务

  • 匿名内部类方式创建线程执行任务

    • 以上2种方式,其实都是通过Runnable的实现类对象来传递任务给线程执行
    • 思考: 是否能不通过实现类对象传递任务给线程执行呢?—>函数式编程
  • Lambda方式创建线程执行任务

    public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("实现类的方式传递任务");}
    }public class Test {public static void main(String[] args) {// 实现Runnable接口的方式创建线程执行任务MyRunnable mr = new MyRunnable();new Thread(mr).start();//匿名内部类方式创建线程执行任务new Thread(new Runnable() {@Overridepublic void run() {System.out.println("匿名内部类的方式传递任务");}}).start();//以上2种方式,其实都是通过Runnable的实现类对象来传递任务给线程执行// 思考: 是否能不通过实现类对象传递任务给线程执行呢?--->函数式编程// 函数式编程: 强调是做什么,而不是以什么形式做//Lambda方式创建线程执行任务new Thread(()->{System.out.println("函数式编程思想方式传递任务");}).start();}
    }
  • Lambda表达式没有特殊功能,就是用来简化代码的

2.3 Lambda表达式的标准格式

  • 标准格式

    • 格式: (参数类型 参数名,参数类型 参数名,...)->{ 代码块 }
  • Lambda的使用前提条件:

    • 接口中只有一个抽象方法的接口,才可以使用Lambda表达式

    • 只有一个抽象方法的接口叫做函数式接口,函数式接口可以使用@FunctionalInterface注解标识

    • eg:

      // A接口就是函数式接口,可以使用Lambda表达式替换实现类对象
      public interface A{void method1();
      }
      // B接口就是函数式接口,可以使用Lambda表达式替换实现类对象
      @FunctionalInterface
      public interface B{void method1();
      }// C接口就不是一个函数式接口,就不能使用Lambda表达式
      public interface C{void method1();void method2();
      }// D接口就是一个函数式接口,可以使用Lambda表达式替换实现类对象
      public interface D{void method1();default void method2(){}...
      }
      
  • 格式说明

    • 小括号中的参数类型,参数个数,参数顺序要和函数式接口中抽象方法的形参列表一致
    • -> 固定格式,表示指向
    • 大括号中的内容其实就是之前重写接口中抽象方法的方法体
  • 案例演示

    • Runnable函数式接口

      public class Test {public static void main(String[] args) {// - Runnable函数式接口new Thread(() -> {System.out.println("任务代码");System.out.println("任务代码");System.out.println("任务代码");System.out.println("任务代码");}).start();}
      }
    • Comparator函数式接口

      public class Test {public static void main(String[] args) {// - Comparator函数式接口ArrayList<Integer> list = new ArrayList<>();list.add(500);list.add(100);list.add(400);list.add(200);list.add(300);// 排序--->升序排序Collections.sort(list, (Integer i1, Integer i2) -> {return i1 - i2;});System.out.println("排序后的集合:" + list);// 排序--->降序排序Collections.sort(list,(Integer o1,Integer o2)->{return o2 - o1;});System.out.println("排序后的集合:" + list);}
      }
  • Lambda使用套路

    • 1.判断该位置上是否可以使用Lambda表达式—>使用前提
    • 2.如果可以使用,直接写上()->{}
    • 3.填充小括号中的内容—>函数式接口中抽象方法的形参一致
    • 4.填充大括号中的内容—>重写函数式接口抽象方法需要的方法体

2.4 Lambda表达式省略格式

  • 省略规则

    • 小括号中参数类型可以省略不写
    • 小括号中只有一个参数,那么小括号也可以省略
    • 大括号中如果只有一条语句,那么大括号,return关键字,分号也可以省略(三个要一起省略)
  • 案例演示

    public class Test {public static void main(String[] args) {// - Runnable函数式接口// 标准格式new Thread(() -> {System.out.println("任务代码1");}).start();// 省略格式new Thread(() -> System.out.println("任务代码2")).start();// - Comparator函数式接口ArrayList<Integer> list = new ArrayList<>();list.add(500);list.add(100);list.add(400);list.add(200);list.add(300);// 排序--->升序排序// 标准格式:/*Collections.sort(list, (Integer i1, Integer i2) -> {return i1 - i2;});*/// 省略格式:Collections.sort(list, ( i1,  i2) -> i1 - i2);System.out.println("排序后的集合:" + list);// 排序--->降序排序// 省略格式:Collections.sort(list,( o1, o2)-> o2 - o1);System.out.println("排序后的集合:" + list);}
    }

2.5 Lambda的表现形式

  • Lambda的表现形式: Lambda表达式会出现在哪些位置

    • 变量形式: 赋值一个Lambda表达式

      public class Test {public static void main(String[] args) {// - 变量形式:Runnable r1 = new Runnable() {@Overridepublic void run() {System.out.println("Runnable匿名内部类--实现类对象");}};Runnable r2 = () -> {System.out.println("Runnable对应的Lambda表达式");};  }
      }
    • 参数形式: 传入Lambda表达式作为实参

      public class Test {public static void main(String[] args) {Runnable r2 = () -> {System.out.println("Runnable对应的Lambda表达式");};// - 参数形式:new Thread(new Runnable() {@Overridepublic void run() {System.out.println("Runnable匿名内部类--实现类对象");}}).start();new Thread(() -> {System.out.println("Runnable对应的Lambda表达式");}).start();new Thread(r2).start();}
      }
    • 返回值形式: 返回一个Lambda表达式(返回值)

      public class Test {public static void main(String[] args) {// - 返回值形式:// - Comparator函数式接口ArrayList<Integer> list = new ArrayList<>();list.add(500);list.add(100);list.add(400);list.add(200);list.add(300);Comparator<Integer> comp = getComparator();Collections.sort(list, comp);System.out.println("排序后:" + list);}public static Comparator<Integer> getComparator() {/*return new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}};*///return (Integer o1, Integer o2)->{return o2 - o1;};// 标准格式return ( o1,  o2)-> o2 - o1;// 省略格式}
      }

第三章 Stream

在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。

3.1 感受Stream流

  • 传统方式操作集合

    • 需求:

       List<String> one = new ArrayList<>();one.add("迪丽热巴");one.add("宋远桥");one.add("苏星河");one.add("老子");one.add("庄子");one.add("黄祺龙");one.add("孙子");one.add("洪七公");
      需求:
      1. 队伍中只要名字为3个字的成员姓名;
      2. 队伍中筛选之后只要前3个人;
      
  • Stream流操作集合

    public class Test {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("洪七公");// 需求:// 1. 队伍中只要名字为3个字的成员姓名;// 1.1 创建一个新的集合,用来存储名字为3个字的姓名ArrayList<String> list1 = new ArrayList<>();// 1.2 循环遍历one这个集合for (String name : one) {// 1.3 在循环中,判断筛选,存储if (name.length() == 3){list1.add(name);}}// 2. 队伍中筛选之后只要前3个人;// 2.1 创建一个新的集合,用来存储前3个ArrayList<String> list2 = new ArrayList<>();// 2.2 循环遍历3次,存储for (int i = 0; i < 3; i++) {String e = list1.get(i);list2.add(e);}System.out.println("list2:"+list2);// list2:[宋远桥, 苏星河, 黄晓明]// Stream流: 获取流--->操作流(过滤)-->得到结果 one.stream().filter(name->name.length()==3).limit(3).forEach(name->System.out.println(name));}
    }

3.2 流式思想概述

  • 概述: 可以将流式思想类比成工厂车间的流水线\河流…

  • 特点:

    • 流一定要搭建好完整的函数模型,函数模型中必须要有终结方法
    • Stream流不能重复操作,也就是一个Stream流只能使用一次
    • Stream流不会存储数据的
    • Stream流不会修改数据源

3.3 获取流方式

  • 根据集合获取流---->Collection集合中有一个获取流的方法public default Stream<E> stream();

    • 根据Collection获取流

      public class Test1_根据Collection集合获取流 {public static void main(String[] args) {// ListList<String> list = new ArrayList<>();list.add("王宝强");list.add("贾乃亮");list.add("谢霆锋");list.add("陈羽凡");// 获取流Stream<String> stream1 = list.stream();// SetSet<String> set = new HashSet<>();set.add("马蓉");set.add("李小璐");set.add("张柏芝");set.add("白百何");// 获取流Stream<String> stream2 = set.stream();}
      }
    • 根据Map获取流

      • 根据Map集合的键获取流

      • 根据Map集合的值获取流

      • 根据Map集合的键值对对象获取流

        public class Test2_根据Map集合获取流 {public static void main(String[] args) {Map<String,String> map = new HashMap<>();map.put("王宝强","马蓉");map.put("贾乃亮","李小璐");map.put("谢霆锋","张柏芝");map.put("陈羽凡","白百何");// - 根据Map集合的键获取流Stream<String> stream1 = map.keySet().stream();//- 根据Map集合的值获取流Stream<String> stream2 = map.values().stream();//- 根据Map集合的键值对对象获取流Stream<Map.Entry<String, String>> stream3 = map.entrySet().stream();}
        }
        
  • 根据数组获取流---->使用Stream流的静态of方法

    • public static <T> Stream<T> of(T... values);

      public class Test3_根据数组获取流 {public static void main(String[] args) {String[] arr = {"王宝强","贾乃亮","谢霆锋","陈羽凡"};// 根据数组元素获取流Stream<String> stream1 = Stream.of(arr);// 直接传值获取流Stream<String> stream2 = Stream.of("王宝强", "贾乃亮", "谢霆锋", "陈羽凡");}
      }

3.4 Stream流常用方法

  • 终结方法: 方法的返回值类型不是Stream流,流中一定要有终结方法,否则无法执行

  • 延迟方法: 方法的返回值类型是Stream流

  • 常用方法:

    • forEach: 逐一处理流中的元素

      public class Test1_forEach {public static void main(String[] args) {/*forEach:void forEach(Consumer<? super T> action);逐一处理流中的元素参数Consumer: 函数式接口*/// 获取流Stream<String> stream = Stream.of("王宝强", "贾乃亮", "谢霆锋", "陈羽凡");// 需求:分别输出流中的所有元素//stream.forEach((String t)->{System.out.println(t);});// 标准格式stream.forEach(t -> System.out.println(t));// 省略格式}
      }
    • count: 统计流中元素的个数

      public class Test2_count {public static void main(String[] args) {/*forEach:long count();统计流中元素的个数*/// 获取流Stream<String> stream = Stream.of("王宝强", "贾乃亮", "谢霆锋", "陈羽凡");// 需求:分别输出流中的所有元素long count = stream.count();System.out.println("stream流元素的个数:"+count);}
      }
    • filter: 根据条件过滤

      public class Test3_filter {public static void main(String[] args) {/*filter:Stream<T> filter(Predicate<? super T> predicate); 根据指定条件过滤,满足条件的元素就组成一个新的流,并返回新的Stream流参数Predicate: 函数式接口---->用来做判断*/// 获取流Stream<String> stream = Stream.of("王宝强", "贾乃亮", "王叔叔","隔壁老王", "谢霆锋", "王小二", "陈羽凡");// 需求:过滤出姓王的元素,并打印输出stream.filter((String t)->{return t.startsWith("王");}).forEach(e->System.out.println(e));}}
    • limit: 取流中前几个元素

      public class Test4_limit {public static void main(String[] args) {/*limit:Stream<T> limit(long maxSize); 取前几个元素注意: 1.参数一般开发中,设置大于0小于流中元素的个数2.如果参数设置小于0的数,就会报异常3.如果参数设置为0,返回的流中没有元素4.如果参数设置大于流中元素个数,返回的流中就包含了流中所有的元素*/// 获取流Stream<String> stream = Stream.of("王宝强", "贾乃亮", "王叔叔","隔壁老王", "谢霆锋", "王小二", "陈羽凡");// 需求: 取前4个元素,打印输出stream.limit(4).forEach(e-> System.out.println(e));//stream.limit(9).forEach(e-> System.out.println(e));// 取流中所有元素//stream.limit(-9).forEach(e-> System.out.println(e));// 报IllegalArgumentException异常}
      }
    • skip: 跳过流中前几个元素

      public class Test5_skip {public static void main(String[] args) {/*skip:Stream<T> skip(long n);跳过前几个元素注意:1.参数一般设置大于0或者小于元素个数2.如果参数设置为0,返回新的流中包含了所有元素3.如果参数设置为大于或者等于元素个数,返回新的流中没有元素4.如果参数设置为小于0,报异常*/// 获取流Stream<String> stream = Stream.of("王宝强", "贾乃亮", "王叔叔","隔壁老王", "谢霆锋", "王小二", "陈羽凡");// 需求: 跳过前4个元素,剩余元素打印输出stream.skip(4).forEach(e-> System.out.println(e));//stream.skip(8).forEach(e-> System.out.println(e));// 没有元素输出//stream.skip(-8).forEach(e-> System.out.println(e));// 报异常}
      }
      
    • map: 映射\转换

      public class Test6_map {public static void main(String[] args) {/*map:<R> Stream<R> map(Function<? super T, ? extends R> mapper); 将流中T类型的元素转换为R类型的元素,返回一个新的流(R)注意:1.T,R的类型可以一致,也可以不一致2.参数Function: 函数式接口-->转换接口*/// 获取流Stream<String> stream = Stream.of("100", "200", "300", "400", "500");// 需求:把流中字符串类型转换为Integer类型,打印输出stream.map((String t)->{return Integer.parseInt(t);}).forEach(e-> System.out.println(e+1));}
      }
      
    • concat: 拼接2个流

      public class Test7_concat {public static void main(String[] args) {/*concat:public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) 拼接2个流中的元素,得到新的流*/// 获取流Stream<String> stream1 = Stream.of("100", "200", "300", "400", "500");// 获取流Stream<String> stream2 = Stream.of("王宝强", "贾乃亮", "王叔叔","隔壁老王");// 拼接Stream.concat(stream1,stream2).forEach(e-> System.out.println(e));}
      }

3.5 Stream综合案例

需求

现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用Stream流,依次进行以下若干操作步骤:

  1. 第一个队伍只要名字为3个字的成员姓名;
  2. 第一个队伍筛选之后只要前3个人;
  3. 第二个队伍只要姓张的成员姓名;
  4. 第二个队伍筛选之后不要前2个人;
  5. 将两个队伍合并为一个队伍;
  6. 根据姓名创建Person对象;
  7. 打印整个队伍的Person对象信息。

两个队伍(集合)的代码如下:

public class DemoArrayListNames {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("洪七公");List<String> two = new ArrayList<>();two.add("古力娜扎");two.add("张无忌");two.add("张三丰");two.add("赵丽颖");two.add("张二狗");two.add("张天爱");two.add("张三");// ....}
}

实现

public class Test {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("洪七公");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> stream1 = one.stream().filter((String t) -> {return t.length() == 3;}).limit(3);// 3. 第二个队伍只要姓张的成员姓名;// 4. 第二个队伍筛选之后不要前2个人;Stream<String> stream2 = two.stream().filter((String t) -> {return t.startsWith("张");}).skip(2);// 5. 将两个队伍合并为一个队伍;// 6. 根据姓名创建Person对象;// 7. 打印整个队伍的Person对象信息。Stream.concat(stream1,stream2).map((String name)->{ return new Person(name);}).forEach(p-> System.out.println(p));}
}

3.6 收集Stream结果

收集到数组中

  • public statci Object[] toArray(); 把流中的元素收集到数组中

    public class Test1_收集到数组中 {public static void main(String[] args) {// 获取流Stream<String> stream = Stream.of("王宝强", "贾乃亮", "王叔叔","隔壁老王", "谢霆锋", "王小二", "陈羽凡");// 需求:过滤出姓王的元素,把结果收集到数组中Object[] arr = stream.filter(name -> name.startsWith("王")).toArray();for (Object o : arr) {System.out.println(o);}}
    }

收集到集合中

  • <R, A> R collect(Collector<? super T, A, R> collector);把流中的元素收集到集合中

    • R: 返回值类型,也就是说R是什么类型,就返回什么类型的集合

    • 参数Collector里面的泛型R确定了该方法的返回值类型

    • 如何得到Collector呢???-----> 工具类Collectors

      • public static <T> Collector<T, ?, List<T>> toList()
      • public static <T> Collector<T, ?, Set<T>> toSet()
      • eg; stream.collect(Collectors.toList()) 收集到List集合
      • eg: ``stream.collect(Collectors.toSet()) 收集到Set集合`
      public class Test1_收集到集合中 {public static void main(String[] args) {// 获取流Stream<String> stream = Stream.of("王宝强", "贾乃亮", "王叔叔", "隔壁老王", "谢霆锋", "王小二", "陈羽凡");// 需求:过滤出姓王的元素,把结果收集到集合中//List<String> list = stream.filter(name -> name.startsWith("王")).collect(Collectors.toList());//System.out.println("list:" + list);Set<String> set = stream.filter(name -> name.startsWith("王")).collect(Collectors.toSet());System.out.println("set:" + set);}
      }
      

总结

必须练习:1.线程6种状态之间的相互切换----->画图2.等待唤醒机制--->如何实现等待唤醒机制,如何分析等待唤醒机制案例的执行流程2.1 有规律的打印i循环和j循环2.2 吃包子案例3.Lambda表达式: 3.1 默写使用Lambda表达式的套路---->4步3.2 默写使用前提3.3 默写省略规则4.Stream流:综合案例---->把结果收集到数组或者集合中- 能够说出线程6个状态的名称新建,可运行,锁阻塞,无限等待,计时等待,被终止- 能够理解等待唤醒案例如何实现等待唤醒机制:- 1.使用锁对象调用wait()方法进入无限等待- 2.使用锁对象调用notify()方法唤醒线程- 3.调用wait(),notify()方法的锁对象要一致分析等待唤醒机制程序的执行- 1.不管是否使用等待唤醒机制,线程的调度都是抢占式- 2.线程进入无限等待,线程就会释放锁,cpu,也不会再去争夺- 3.唤醒其他线程,当前唤醒线程是不会释放锁,cpu的- 4.无限等待线程被唤醒,拿到锁对象后,会从进入无限等待的位置继续往下执行- 能够掌握Lambda表达式的标准格式与省略格式Lambda使用套路- 1.判断该位置上是否可以使用Lambda表达式--->使用前提- 2.如果可以使用,直接写上()->{}- 3.填充小括号中的内容--->函数式接口中抽象方法的形参一致- 4.填充大括号中的内容--->重写函数式接口抽象方法需要的方法体省略规则- 小括号中参数类型可以省略不写- 小括号中只有一个参数,那么小括号也可以省略- 大括号中如果只有一条语句,那么大括号,return关键字,分号也可以省略(三个要一起省略)使用前提: 函数式接口- 能够通过集合、映射或数组方式获取流使用Collection的stream方法使用Stream流的of方法- 能够掌握常用的流操作forEach,count,filter,limit,skip,concat,map
- 能够将流中的内容收集到集合和数组中Object[] toArray();stream.collect(Collectors.toList()) 收集到List集合stream.collect(Collectors.toSet()) 收集到Set集合

【线程状态、等待与唤醒、Lambda表达式、Stream流】相关推荐

  1. JDK1.8新特性之Lambda表达式+Stream流+函数式接口

    一.Lambda表达式 Lambda表达式,是JDK1.8引入的一种语法,这种语法可以对匿名内部类的写法,进行简写. 1.快速入门 package org.westos.demo2;import ja ...

  2. 第四章 函数式编程(Lambda表达式Stream流)

    一.Lambda表达式 特点:是匿名函数 2是可传递 匿名函数:需要一个函数,但又不想命名一个函数的场景下使用lambda表达式,使用lambda表达式时函数内容应该简单 可传递:将lambda表达式 ...

  3. lambda表达式——Stream管道流的map操作

    lambda表达式--Stream管道流的map操作 一.回顾Stream管道流map的基础用法 二.处理非字符串类型集合元素 三.再复杂一点:处理对象数据格式转换 四.flatMap 一.回顾Str ...

  4. 精通lambda表达式:java多核编程_Java8 Lambda表达式和流操作如何让你的代码变慢5倍...

    有许许多多关于 Java 8 中流效率的讨论,但根据 Alex Zhitnitsky 的测试结果显示:坚持使用传统的 Java 编程风格--iterator 和 for-each 循环--比 Java ...

  5. Java8 Lambda表达式和流操作如何让你的代码变慢5倍

    当本文博主也想尝试着去用stream的方式去处理数据,毕竟代码看上去很简洁,很爽,但是,我就写了这么简单的一句: List<DevUsbUpgradeResult> list = inse ...

  6. java-线程安全问题,线程实现线程同步,线程状态,等待唤醒机制,生产者消费者模型

    目录标题 解决线程安全问题手段:线程同步 实现同步操作步骤 1.同步代码块 2.同步方法 静态同步方法 3.Lock接口的锁机制 线程状态 生产者消费者模型:等待唤醒 等待唤醒案例 线程安全问题是不能 ...

  7. java线程唤醒与等待_Java线程的等待与唤醒

    生产者和消费者必须使用同步代码块包裹起来,保证等待和唤醒只能有一个执行,同步使用的锁对象必须保证唯一 Thread中重要方法 void wait() 在其他线程调用此对象的notify()方法或not ...

  8. Android消息机制(Handler机制) - 线程的等待和唤醒

    我们都知道,Android的Handler机制,会在线程中开启消息循环,不断的从消息队列中取出消息,这个机制保证了主线程能够及时的接收和处理消息. 通常在消息队列中(MessageQueue)中没有消 ...

  9. 线程的等待和唤醒机制

    目录 第一种方式:synchronized + wait + notify: 第二种方式:Lock + await + signal : 第三种方式:LockSupport + park +unpar ...

  10. java 8 stream_深度分析:java8的新特性lambda和stream流,看完你学会了吗?

    1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...

最新文章

  1. ORACLE数据库对比表结构
  2. 如何利用竞价的思维去做seo?
  3. 用 Docker 构建、运行、发布来一个 Spring Boot 应用
  4. 【数据字典】国标数据字典
  5. Java EE 8发生了什么?
  6. 汇编语言-016(SCASB 、STOSB 、LODSD 、数组中的取值、二维数组操作、冒泡排序和二分查找、CMPSW )
  7. ADSL路由器的设置
  8. Python面试题大全(四):数据库篇
  9. delphi mysql 加密_Delphi对Access文件加密
  10. 5个让你的404页面变的更加实用的技巧
  11. c语言中的scanf在java中应该怎么表达,Scanner类。
  12. 计算机硬件参数的工具软件,电脑硬件参数修改工具
  13. 对高级程序设计语言的基本理解
  14. 【转】Delphi配置系统未能初始化
  15. html弹窗确认取消公告代码,javascript实现确定和取消提示框效果
  16. C++ 多线程CreateThread LPVOIDlpParameter传递多个参数
  17. 金蝶系统服务器名称填什么,金蝶怎样输入服务器地址
  18. uvm event 事件机制
  19. 111完美邮箱品牌域名——数字域名111.com
  20. idea 离线安装translation 谷歌翻译

热门文章

  1. 学习汇编记录Day3——汇编指令
  2. tortoise git revert的坑
  3. 云计算会不会被边缘计算取而代之
  4. MySQL with 写法
  5. 全国哀悼日网站都成黑白色实现
  6. 路由器的三种配置方式
  7. Cisco Packet Tracer路由器的基本命令
  8. java 验证码 仿12306_Java仿12306图片验证码
  9. 电脑设备打印机驱动安装失败如何解决
  10. Silverlight/Windows8/WPF/WP7/HTML5周学习导读(9月10日-9月16日)