【线程状态、等待与唤醒、Lambda表达式、Stream流】
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流,依次进行以下若干操作步骤:
- 第一个队伍只要名字为3个字的成员姓名;
- 第一个队伍筛选之后只要前3个人;
- 第二个队伍只要姓张的成员姓名;
- 第二个队伍筛选之后不要前2个人;
- 将两个队伍合并为一个队伍;
- 根据姓名创建
Person
对象; - 打印整个队伍的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流】相关推荐
- JDK1.8新特性之Lambda表达式+Stream流+函数式接口
一.Lambda表达式 Lambda表达式,是JDK1.8引入的一种语法,这种语法可以对匿名内部类的写法,进行简写. 1.快速入门 package org.westos.demo2;import ja ...
- 第四章 函数式编程(Lambda表达式Stream流)
一.Lambda表达式 特点:是匿名函数 2是可传递 匿名函数:需要一个函数,但又不想命名一个函数的场景下使用lambda表达式,使用lambda表达式时函数内容应该简单 可传递:将lambda表达式 ...
- lambda表达式——Stream管道流的map操作
lambda表达式--Stream管道流的map操作 一.回顾Stream管道流map的基础用法 二.处理非字符串类型集合元素 三.再复杂一点:处理对象数据格式转换 四.flatMap 一.回顾Str ...
- 精通lambda表达式:java多核编程_Java8 Lambda表达式和流操作如何让你的代码变慢5倍...
有许许多多关于 Java 8 中流效率的讨论,但根据 Alex Zhitnitsky 的测试结果显示:坚持使用传统的 Java 编程风格--iterator 和 for-each 循环--比 Java ...
- Java8 Lambda表达式和流操作如何让你的代码变慢5倍
当本文博主也想尝试着去用stream的方式去处理数据,毕竟代码看上去很简洁,很爽,但是,我就写了这么简单的一句: List<DevUsbUpgradeResult> list = inse ...
- java-线程安全问题,线程实现线程同步,线程状态,等待唤醒机制,生产者消费者模型
目录标题 解决线程安全问题手段:线程同步 实现同步操作步骤 1.同步代码块 2.同步方法 静态同步方法 3.Lock接口的锁机制 线程状态 生产者消费者模型:等待唤醒 等待唤醒案例 线程安全问题是不能 ...
- java线程唤醒与等待_Java线程的等待与唤醒
生产者和消费者必须使用同步代码块包裹起来,保证等待和唤醒只能有一个执行,同步使用的锁对象必须保证唯一 Thread中重要方法 void wait() 在其他线程调用此对象的notify()方法或not ...
- Android消息机制(Handler机制) - 线程的等待和唤醒
我们都知道,Android的Handler机制,会在线程中开启消息循环,不断的从消息队列中取出消息,这个机制保证了主线程能够及时的接收和处理消息. 通常在消息队列中(MessageQueue)中没有消 ...
- 线程的等待和唤醒机制
目录 第一种方式:synchronized + wait + notify: 第二种方式:Lock + await + signal : 第三种方式:LockSupport + park +unpar ...
- java 8 stream_深度分析:java8的新特性lambda和stream流,看完你学会了吗?
1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...
最新文章
- ORACLE数据库对比表结构
- 如何利用竞价的思维去做seo?
- 用 Docker 构建、运行、发布来一个 Spring Boot 应用
- 【数据字典】国标数据字典
- Java EE 8发生了什么?
- 汇编语言-016(SCASB 、STOSB 、LODSD 、数组中的取值、二维数组操作、冒泡排序和二分查找、CMPSW )
- ADSL路由器的设置
- Python面试题大全(四):数据库篇
- delphi mysql 加密_Delphi对Access文件加密
- 5个让你的404页面变的更加实用的技巧
- c语言中的scanf在java中应该怎么表达,Scanner类。
- 计算机硬件参数的工具软件,电脑硬件参数修改工具
- 对高级程序设计语言的基本理解
- 【转】Delphi配置系统未能初始化
- html弹窗确认取消公告代码,javascript实现确定和取消提示框效果
- C++ 多线程CreateThread LPVOIDlpParameter传递多个参数
- 金蝶系统服务器名称填什么,金蝶怎样输入服务器地址
- uvm event 事件机制
- 111完美邮箱品牌域名——数字域名111.com
- idea 离线安装translation 谷歌翻译