Stream流
1 Stream流思想
Stream是专门用来处理容器数据的API,是一种以流水线作业的模式来处理数据的思想。
Stream流使用的过程中,含有三种类型的方法:
- 获取Stream流
创建一条流水线,并把数据放到流水线上准备进行操作 - 中间方法
流水线上的操作。一次操作完毕之后,还可以继续进行其他操作。 - 终结方法
一个Stream流只能有一个终结方法,是流水线上的最后一个操作
2 Stream的获取方式
单列集合
可以使用Collection接口中的默认方法stream()生成流
default Stream<E> stream()双列集合
间接的生成流
可以先通过keySet或者entrySet获取一个Set集合,再获取Stream流
数组
Arrays中的静态方法stream 生成流
3 Stream流中间方法
Stream<T> filter(Predicate predicate):用于对流中的数据进行过滤Predicate接口中的方法 : boolean test(T t):对给定的参数进行判断,返回一个布尔值Stream<T> limit(long n):获取前n个元素Stream<T> skip(long n):跳过n个数的数据static <T> Stream<T> concat(Stream a, Stream b):合并a和b两个流为一个流Stream<T> distinct():去除流中重复的元素。依赖(hashCode和equals方法)Stream<T> sorted () : 将流中元素按照自然排序的规则排序Stream<T> sorted (Comparator<? super T> comparator) : 将流中元素按照自定义比较器规则排序<R> Stream<R> map(Function<? super T, ? extends R> mapper): 将流中数据映射为新的数据
使用参考代码:
public class StreamDemo3 {public static void main(String[] args) {//ArrayList<String> list = new ArrayList<>(List.of("曹操", "曹孟德", "曹阿瞒", "曹阿瞒", "曹阿瞒", "刘备", "刘玄德", "刘皇叔"));ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "曹操", "曹孟德", "曹阿瞒", "曹阿瞒", "曹阿瞒", "刘备", "刘玄德", "刘皇叔");/* list.stream().forEach( (String str)-> {System.out.println(str);} );*///遍历流中数据 foreachlist.stream().forEach(str -> System.out.println(str));System.out.println("=====");//筛选流中数据:filter Stream<T> filter(Predicate predicate)list.stream().filter(str -> str.startsWith("曹")).filter(str -> str.length() == 3).forEach(str -> System.out.println(str));System.out.println("=====");// Stream<T> limit(long maxSize):截取指定参数个数的数据// Stream<T> skip(long n):跳过指定参数个数的数据Stream.of("A", "B", "C", "D", "E", "F").limit(5) //取前5个.skip(2)//跳过前2个.forEach(str -> System.out.println(str));System.out.println("=====");// 4 static <T> Stream<T> concat(Stream a, Stream b):合并a和b两个流为一个流Stream<String> s1 = Stream.of("A", "B", "C");Stream<String> s2 = Stream.of("X", "Y", "Z");Stream.concat(s1, s2).forEach(System.out::println);System.out.println("=====");// 5 Stream<T> distinct():去除流中重复的元素。依赖(hashCode和equals方法)Stream.of("A", "B", "C", "A", "B", "C").distinct() //可以去重.forEach(System.out::println);// 6 Stream<T> sorted () : 将流中元素按照自然排序的规则排序System.out.println("=====");Stream.of("A", "C", "B", "E", "D").sorted().forEach(System.out::println);System.out.println("=====");// 7 Stream<T> sorted (Comparator<? super T> comparator) : 将流中元素按照自定义比较器规则排序Stream.of("A", "C", "B", "E", "D")//.sorted(Comparator.reverseOrder()).sorted((o1, o2) -> o2.compareTo(o1)).forEach(System.out::println);// 8 数据的转换 map// <R> Stream<R> map(Function<T, R> mapper);// Function函数式接口中存在抽象方法 :// R apply (T t) 将T (流中原有数据类型)类型转换为R(将要转换的类型)类型//需求: 将字符串 T 转换为学生类型 RStream.of("张三-18", "李四-20").map(str -> {String[] arr = str.split("-");return new Student(arr[0], Integer.parseInt(arr[1]));}).forEach(System.out::println);}}class Student {String name;int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}}
4 Stream流终结方法
1 void forEach(Consumer action):对此流的每个元素执行操作Consumer接口中的方法 void accept(T t):对给定的参数执行此操作2 long count():返回此流中的元素个数3 流收集方法,看下一个知识点
使用参考代码:
public class StreamDemo4 {public static void main(String[] args) {//ArrayList<String> list = new ArrayList<>(List.of("曹操", "曹孟德", "曹阿瞒", "曹阿瞒", "曹阿瞒", "刘备", "刘玄德", "刘皇叔"));ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "曹操", "曹孟德", "曹阿瞒", "曹阿瞒", "曹阿瞒", "刘备", "刘玄德", "刘皇叔");//集合本身也有一个forEach方法用来做遍历//list.forEach(str-> System.out.println(str));// 1 void forEach(Consumer action):对此流的每个元素执行操作Stream<String> s1 = list.stream();s1.forEach(str-> System.out.println(str));// 2 long count():返回此流中的元素个数//流只能用一次,long count = list.stream().limit(10).skip(5).count();System.out.println("count = " + count);}}//输出结果曹操曹孟德曹阿瞒曹阿瞒曹阿瞒刘备刘玄德刘皇叔count = 3
5 流收集方法
Stream流在操作的过程中,不会导致原集合或者数组内容发生改变。如果想要流系列操作后的结果,我们需要进行收集。
R collect(Collector collector) : 此方法只负责收集流中的数据 , 创建集合添加数据动作需要依赖于参数工具类Collectors提供了具体的收集方式public static <T> Collector toList():把元素收集到List集合中public static <T> Collector toSet():把元素收集到Set集合中public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
收集到List集合,Set集合参考
import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Set;import java.util.stream.Collector;import java.util.stream.Collectors;public class StreamDemo6 {public static void main(String[] args) {//ArrayList<String> list = new ArrayList<>(List.of("曹操", "曹孟德", "曹阿瞒", "曹阿瞒", "刘备", "刘玄德", "刘皇叔")); //JDK9之上支持ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "曹操", "曹孟德", "曹阿瞒", "曹阿瞒", "刘备", "刘玄德", "刘皇叔");// 使用stream流 , 过滤获取姓 曹 且 三个字的名字// collect负责收集元素 , 不负责创建List集合对象// 如果需要创建List集合对象需要依赖于Collectors.toList()List<String> caoList = list.stream().filter(name -> name.startsWith("曹") && name.length() == 3).collect(Collectors.toList());System.out.println("caoList = " + caoList);//[曹孟德, 曹阿瞒, 曹阿瞒]System.out.println("===============");// public static <T> Collector toSet():把元素收集到Set集合中Set<String> caoSet = list.stream().filter(name -> name.startsWith("曹") && name.length() == 3).collect(Collectors.toSet());System.out.println("caoSet = " + caoSet);//[曹阿瞒, 曹孟德]}}
收集到Map集合参考
import java.util.ArrayList;import java.util.Collections;import java.util.Map;import java.util.stream.Collectors;/*1 创建一个ArrayList集合,并添加以下字符串。字符串中前面是姓名,后面是年龄"zhangsan,23""lisi,24""wangwu,25"2 保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值需求1 : 保留年龄大于等于24岁的人需求2 : 并将结果收集到Map集合中,姓名为键,年龄为值收集方法 :Collectors工具类的public static Collector toMap(Function keyMapper , Function valueMapper):把元素收集到Map集合中*/public class StreamDemo7 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("zhangsan,23");list.add("lisi,24");list.add("wangwu,25");// 需求1 : 保留年龄大于等于24岁的人Map<String, String> map = list.stream().filter(str -> Integer.parseInt(str.split(",")[1]) >= 24)//.forEach(System.out::println);.collect(Collectors.toMap(//键的Functionstr -> str.split(",")[0],//值的Functionstr -> str.split(",")[1]));System.out.println("map = " + map);}}
多线程
1 名词概念
- 并行:在同一时刻,有多个任务在多个CPU上同时执行。
- 并发:在同一时刻,有多个任务在单个CPU上交替执行。
- 进程:进程简单地说就是在多任务操作系统中,每个独立执行的程序,所以进程也就是“正在进行的程序”。(Windows系统中,我们可以在任务管理器中看到进程)
- 线程:线程是程序运行的基本执行单元。当操作系统执行一个程序时,会在系统中建立一个进程,该进程必须至少建立一个线程(这个线程被称为主线程)作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个线程。
- 多线程:
- 硬件角度:现代CPU能够同时处理多个线程任务
- 软件角度:一个进程,可以同时开启多个线程执行不同任务

可以提高系统资源的利用率,及解决问题的效率。
2 线程的创建方式
java.lang.Thread 是线程类,可以用来给进程创建线程处理任务使用。要使用线程先介绍两个比较重要的方法:
- publicvoid run() : 线程执行任务的方法,是线程启动后第一个执行的方法
- publicvoid start() : 启动线程的方法,线程对象调用该方法后,Java虚拟机就会调用此线程的run方法。
线程的创建方式1:继承Thread方式
基本步骤:
- 创建一个类继承Thread类。
- 在类中重写run方法(线程执行的任务放在这里)
- 创建线程对象,调用线程的start方法开启线程。
代码参考:
需求 :
我们启动一个Java程序,其实默认就存在一个主线程(main方法所在线程)
接下来,我们在主线程启动一个线程,打印1到100的数字,主线程启动完线程后又打印1到100的数字。
此时主线程和启动的线程在并发执行,观察控制台打印的结果。
public class MyThread01 {public static void main(String[] args) {// 3 创建线程对象,调用线程的start方法开启线程。Thread01 t1 = new Thread01();t1.start();//当主线程开启了t1线程后,不会等待线程t1执行完//继续执行后续代码for (int i = 0; i < 100; i++) {System.out.println("旺财:" + i);}}}//1 创建一个类继承Thread类。class Thread01 extends Thread {//2 在类中重写run方法(线程执行的任务放在这里)@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("小强:" + i);}}}
线程的创建方式2 : 实现Runable方式
实现步骤如下:
- 定义任务类实现Runnable,并重写run方法
- 创建任务对象
- 使用含有Runnable参数的构造方法,创建线程对象并指定任务。
- 调用线程的start方法,开启线程。
代码参考
需求 :
我们启动一个Java程序,其实默认就存在一个主线程(main方法所在线程)
接下来,我们在主线程启动一个线程,打印1到100的数字,主线程启动完线程后又打印1到100的数字。
此时主线程和启动的线程在并发执行,观察控制台打印的结果。
public class MyThread02 {public static void main(String[] args) {//2 创建任务对象MyTask m1 = new MyTask();// 3 创建Thread类型的对象 , Thread类的构造方法需要接受一个Runnable实现类对象//public Thread(Runnable target) : 接受一个Runnable接口的实现类对象Thread t1 = new Thread(m1);//4 调用线程的start方法,开启线程t1.start();//使用Lambda直接实现Runnable方式Thread t2 = new Thread(()-> System.out.println("Lambda 实现Runnable "));t2.start();//主线程干活for (int i = 0; i < 100; i++) {System.out.println("旺财:" + i);}}}//1 定义任务类,实现Runnable,并重写run方法class MyTask implements Runnable {@Overridepublic void run() {//子线程干活for (int i = 0; i < 100; i++) {System.out.println("小强:" + i);}}}
| 优点 | 缺点 | |
|---|---|---|
| 实现Runnable | 扩展性强,实现该接口的同时还可以继承其他的类。 | 编程相对复杂,不能直接使用Thread类中的方法 |
| 继承Thread | 编程比较简单,可以直接使用Thread类中的方法 | 可扩展性较差, 不能再继承其他的类 |
3 Thread常见方法
public String getName():返回此线程的名称public void setName(String name):将此线程的名称更改为等于参数 name ,通过构造方法也可以设置线程名称public static Thread currentThread() :返回对当前正在执行的线程对象的引用public static void sleep(long time):让线程休眠指定的时间,单位为毫秒。 1s = 1000mspublic void join() : 具备阻塞作用 , 等待这个线程死亡,才会执行其他线程public final void setPriority(int newPriority) 设置线程的优先级public final int getPriority() 获取线程的优先级
4 线程调度
线程有两种调度模型
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
:Java使用的是抢占式调度模型
后续持续更新中。。。。
- Lambda表达式
Lambda表达式
1 函数式编程思想
在数学中,函数就是有输入量、输出量的一套计算方案。在编程技术中,函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数。我们经常把支持函数式编程的编码风格称为Lambda表达式。
体验Lambda表达式
import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;/*Lambda表达式体验 :*/public class LambdaDemo {public static void main(String[] args) {//将集合要以降序排序List<Integer> list = new ArrayList<>();list.add(100);list.add(300);list.add(200);//集合以降序排序(以前的做法:匿名对象)Collections.sort(list, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}});//集合以降序排序(新做法:Lambda表达式)Collections.sort(list,(Integer o1, Integer o2) -> {return o2 - o1;});}}
2 函数式接口
只有一个抽象方法需要重写的接口就称为函数式接口。函数式接口允许存在其他的静态方法,默认方法,私有方法等,只对抽象方法有限制。
为了标识接口是一个函数式接口,可以在接口之上加上一个注解: @FunctionalInterface以示区别。
在JDK中 java.util.function 包中的所有接口都是函数式接口。
我们之前学习的Comparator及后续学习线程时的Runnable也是函数式接口。
@FunctionalInterfacepublic interface Runnable {public abstract void run();}
我们也可以自定函数式接口
@FunctionalInterfacepublic interface Swim {public abstract void swimming();}
小结:
- 接口中只有一个抽象方法是需要被实现的就是函数式接口
- 函数式接口通常会有一个标志性注解:@FunctionalInterface
3 Lambda表达式格式
Lambda表达式其实就是对函数式接口匿名内部类的简写,简写到只有一个方法的重写。因此,Lambda表达式所表示的其实就是对函数式接口中抽象方法的重写。
Lambda表达式的标准语法:( 形参列表 ) -> { 方法体 }
就是将匿名内部类一直简化到只有一个方法的存在,而且方法可以进一步简化到只有参数列表,和方法体,中间加上语法箭头。
Lambda表达式的格式
格式:( 形式参数 ) -> { 代码块 }形式参数: 和要写的函数式接口中抽象方法的参数列表一样->: 由英文中画线和大于符号组成,固定写法。方法体: 重写抽象方法的方法体
4 Lambda 省略规则
- 参数类型可以省略,但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return ```java
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List;
/
Lambda表达式体验 :
/
public class LambdaDemo {
public static void main(String[] args) {
//将集合要以降序排序
List
//省略前Collections.sort(list,(Integer o1, Integer o2) -> {return o2 - o1;});//省略后Collections.sort(list, ( o1, o2) -> o2 - o1);}
}
5 Lambda表达式和匿名内部类区别
- 所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体
Lambda表达式:只能是函数式接口 - 使用限制不同
如果接口中有且仅有一个抽象方法需要重写,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式 - 实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
Hi,这是一条已归档的小记
什么是归档?
当你不再需要一篇小记展示在信息列表,但又不想删掉它时,可以将它进行归档,归档后的小记将进入已归档列表。
如何归档小记?
点击小记右边的…按钮,点击「归档」,点击左侧多选按钮还可进行批量归档操作。
