Java高级_Day19(Stream,Optional,类加载器)
Stream
Java8 新特性:Lambda表达式 StreamAPI
Stream是Java中处理集合的关键的一个抽象的概念,它可以指定我们希望对集合执行的一些操作,可以执行一些非常复杂的查找 过滤和映射数据的相关操作,使用Stream对集合数据进行操作,类似于SQL与查询数据库。可以是用Stream API来执行相关操作.
Stream提供了一种高效且以用的处理数据的方式
为什么要使用Stream:
随着技术的发展,特别是NOSQL技术的出现,导致数据的处理,需要延迟到java层面。
Stream和Collection集合的区别:
Collection是一种静态的内存数据结构,面向内存,存储在内存中
Stream是对数据进行相关计算,面向CPU的,通过CPU实现数据的处理计算
集合侧重点数据,Stream的侧重点是计算
Stream的常见操作
- 创建Stream
一个数据源(集合 数组等)获取一个Stream - 中间操作
中间操作是一个操作链,对数据源进行相应的处理 - 终止操作
一旦执行终止操作,中间操作链就会产生执行的结果,之后不会再被使用
Stream的创建:
方式一:通过集合
返回值类型 | 方法 |
---|---|
default Stream | parallelStream() 返回可能并行的 Stream与此集合作为其来源。 |
default Stream | stream() 返回以此集合作为源的顺序 Stream 。 |
@Test
public void createSteam1(){
List<Emp> empList = new ArrayList<>();
//顺序 Stream
Stream<Emp> empStream = empList.stream();
//并行的 Stream
Stream<Emp> empStream1 = empList.parallelStream();
}
方式二:通过数组
返回值类型 | 方法 |
---|---|
static Stream | stream(T[] array) 返回顺序Stream与指定的数组作为源。 |
static IntStream | stream(int[] array) 返回顺序IntStream与指定的数组作为源。 |
static DoubleStream | stream(double[] array) 返回顺序DoubleStream与指定的数组作为源。 |
static LongStream | stream(long[] array) 返回顺序LongStream与指定的数组作为源。 |
@Test
public void createSteam2(){
//基本数据类型
int[] arr = new int[]{1,2,3,4,5,6};
IntStream intStream = Arrays.stream(arr);
// 对象数组
Emp[] emps = new Emp[2];
Emp emp1 = new Emp(1001,"admin");
Emp emp2 = new Emp(1002,"tom");
emps[0] = emp1;
emps[1] = emp2;
Stream<Emp> empStream = Arrays.stream(emps);
}
方式三:通过Stream的of()
返回值类型 | 方法 |
---|---|
static Stream | of(T... values) 返回其元素是指定值的顺序排序流。 |
static Stream | of(T t) 返回包含单个元素的顺序 Stream |
@Test
public void createSteam3(){
Stream<Integer> stream = Stream.of(1,2,3,4,5);
}
方式4:创建一个无限流(了解)
返回值类型 | 方法 |
---|---|
static Stream | iterate(T seed, UnaryOperator<T> f) 返回有序无限连续 Stream由函数的迭代应用产生 f至初始元素 seed ,产生 Stream包括 seed , f(seed) , f(f(seed)) ,等 |
static Stream | generate(Supplier<T> s) 返回无限顺序无序流,其中每个元素由提供的 Supplier 。 |
@Test
public void createSteam4(){
//迭代
Stream<Integer> integerStream = Stream.iterate(0,x->x+2);
integerStream.limit(10).forEach(System.out::println);
// 生成
Stream<Double> doubleStream = Stream.generate(Math::random);
doubleStream.limit(10).forEach(System.out::println);
}
Stream的中间操作:
中间操作是一个链式操作,在中间操作的过程中,不会产生任何结果,直到执行终止操作,然后进行一次性的处理,称为”惰性求值”
- 筛选和切片
返回值类型 | 方法 |
---|---|
Stream | filter(Predicate<? super T> predicate) 接收一个lambda表达式,排除一些元素 |
Stream | distinct() 筛选,通过流生成元素的hashcode和equals去除重复元素 |
Stream | limit(long maxSize) 截断流,限制流中的元素的数量不能超过给定值 |
Stream | skip(long n) 跳过元素 返回一个抛弃前n个元素的流,如果流中的元素小于n 就会返回一个空的Stream 与limit进行了一个互补的 |
package cn.lanqiao.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @author Hou
*/
public class Emp {
private int num;
private String name;
private int age;
private int salary;
public Emp() {
}
public Emp(int num, String name) {
this.num = num;
this.name = name;
}
public Emp(int num, String name, int age, int salary) {
this.num = num;
this.name = name;
this.age = age;
this.salary = salary;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
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;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Emp{" +
"num=" + num +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Emp emp = (Emp) o;
return num == emp.num &&
age == emp.age &&
salary == emp.salary &&
Objects.equals(name, emp.name);
}
@Override
public int hashCode() {
return Objects.hash(num, name, age, salary);
}
public static List<Emp> empList(){
List<Emp> empList = new ArrayList<>();
Emp e1 = new Emp(1001,"马云",50,12000);
Emp e2 = new Emp(1001,"马化腾",40,8000);
Emp e3 = new Emp(1001,"雷军",45,9000);
Emp e4 = new Emp(1001,"李彦宏",35,15000);
Emp e5 = new Emp(1001,"任正非",60,20000);
empList.add(e1);
empList.add(e2);
empList.add(e3);
empList.add(e4);
empList.add(e5);
return empList;
}
}
package cn.lanqiao.stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.stream.Stream;
/**
* @author Hou
*/
public class StreamDemo2 {
Stream<Emp> empStream;
List<Emp> empList;
@BeforeEach
public void init(){
empList = Emp.empList();
//创建stream
empStream = empList.stream();
}
//获取薪资大于10000的员工
@Test
public void filterTest(){
//执行过滤操作
empStream.filter(e->e.getSalary()>10000).forEach(System.out::println);
}
// 获取集合中的3名员工
@Test
public void limitTest(){
empStream.limit(3).forEach(System.out::println);
}
//跳过指定的元素
@Test
public void skipTest(){
empStream.skip(3).forEach(System.out::println);
}
//筛选 distinct 注意必须重写对象hashCode和Equals
@Test
public void distinctTest(){
empList.add(new Emp(1010,"刘强东",40,8000));
empList.add(new Emp(1010,"刘强东",40,8000));
empList.add(new Emp(1010,"刘强东",40,8000));
empList.add(new Emp(1010,"刘强东",40,8000));
empList.add(new Emp(1010,"刘强东",40,8000));
// 去除集合中的重复元素
empStream.distinct().forEach(System.out::println);
}
}
- 映射
返回值类型 | 方法 |
---|---|
Stream | map(Function<? super T,? extends R> mapper) 返回由给定函数应用于此流的元素的结果组成的流。 |
DoubleStream | mapToDouble(ToDoubleFunction<? super T> mapper) 返回一个 DoubleStream ,其中包含将给定函数应用于此流的元素的结果。 |
IntStream | mapToInt(ToIntFunction<? super T> mapper) 返回一个 IntStream ,其中包含将给定函数应用于此流的元素的结果。 |
LongStream | mapToLong(ToLongFunction<? super T> mapper) 返回一个 LongStream ,其中包含将给定函数应用于此流的元素的结果。 |
Stream | flatMap(Function<? super T,? extends Stream<? extends R>> mapper) 返回由通过将提供的映射函数应用于每个元素而产生的映射流的内容来替换该流的每个元素的结果的流。 |
@Test
public void mapTest1(){
List<String> list = Arrays.asList("aa","bb","cc","dd");
list.stream().map(str->str.toUpperCase()).forEach(System.out::println);
//获取员工姓名的长度大于2的员工
Stream<String> nameStream = empStream.map(Emp::getName);
nameStream.filter(name->name.length() > 2).forEach(System.out::println);
}
@Test
public void mapTest2(){
List<String> list = Arrays.asList("aa","bb","cc","dd");
Stream<Stream<Character>> ss = list.stream().map(StreamDemo3::fromStringToStream);
ss.forEach(s->s.forEach(System.out::println));
}
@Test
public void mapTest3(){
List<String> list = Arrays.asList("aa","bb","cc","dd");
Stream<Character> ss = list.stream().flatMap(StreamDemo3::fromStringToStream);
ss.forEach(System.out::println);
}
// 将字符串中的多个字符构成的集合转换为Stream
public static Stream<Character> fromStringToStream(String str){
List<Character> list = new ArrayList<>();
char[] charArray= str.toCharArray();
for(Character c : charArray){
list.add(c);
}
return list.stream();
}
- 排序
返回值类型 | 方法 |
---|---|
Stream | sorted() 返回由此流的元素组成的流,根据自然顺序排序。 |
Stream | sorted(Comparator<? super T> comparator) 返回由该流的元素组成的流,根据提供的 Comparator进行排序。 |
// 排序 自然排序
@Test
public void sortedTest(){
List<Integer> list = Arrays.asList(21,12,25,42,20,2,35);
list.stream().sorted().forEach(System.out::println);
}
// 对自定义对象的定制排序 对员工按照年龄排序 如果年龄相同 则按照薪资排序
@Test
public void sortedTest2(){
empStream.sorted((e1,e2)->{
Integer ageValue = Integer.compare(e1.getAge(),e2.getAge());
if(ageValue != 0){
return ageValue;
}else{
return Integer.compare(e1.getSalary(),e2.getSalary());
}
}).forEach(System.out::println);
}
终止操作:
- 匹配和查找:
返回值类型 | 方法 |
---|---|
boolean | allMatch(Predicate<? super T> predicate) 检查是否有匹配的元素 |
boolean | anyMatch(Predicate<? super T> predicate) 检查至少有一个匹配元素 |
boolean | noneMatch(Predicate<? super T> predicate) 检查是否没有匹配元素 |
Optional | findAny() 返回任意一个元素 |
Optional | findFirst() 返回第一个元素 |
long count() | 返回此流中的元素数 void forEach(Consumer<? super T> action) |
对此流的每个元素执行操作。
Optional|max(Comparator<? super T> comparator)
根据提供的 Comparator返回此流的最大元素。
Optional|min(Comparator<? super T> comparator)
根据提供的 Comparator返回此流的最小元素。
@Test
// 检查员工的年龄是否都大于18岁
public void test1(){
boolean allMatch = empStream.allMatch(e->e.getAge()> 18);
System.out.println(allMatch);
}
@Test
// 检查员工的薪资是否有低于5000
public void test2(){
boolean anyMatch = empStream.anyMatch(e->e.getSalary()<5000);
System.out.println(anyMatch);
}
@Test
// 检查员工的姓名中是否存在姓”张“
public void test3(){
boolean noneMatch = empStream.noneMatch(e->e.getName().startsWith("张"));
System.out.println(noneMatch);
}
@Test
// 返回第一个元素
public void test4(){
Optional<Emp> empOptional = empStream.findFirst();
System.out.println(empOptional);
}
@Test
// 返回任意一个元素
public void test5(){
Optional<Emp> empOptional = empList.parallelStream().findAny();
System.out.println(empOptional);
}
- 归约:
返回值类型 | 方法 |
---|---|
T | reduce(T identity, BinaryOperator<T> accumulator) 将流中的元素反复的处理,得到一个值T |
Optional | reduce(BinaryOperator<T> accumulator) 将流中的元素反复的处理,得到一个值Optional |
@Test
// 计算1--10的自然数的和
public void test6(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer s = list.stream().reduce(0,Integer::sum);
System.out.println(s);
}
@Test
// 计算所有员工的薪资总和
public void test7(){
Stream<Integer> salarys = empStream.map(Emp::getSalary);
Optional<Integer> salarySum = salarys.reduce((d1,d2)->d1 +d2);
System.out.println(salarySum);
}
- 收集:
返回值类型 | 方法 |
---|---|
<R,A> R | collect(Collector<? super T,A,R> collector) 使用 Collector对此流的元素执行 mutable reduction Collector 。 |
@Test
// 查找员工的薪资大于9000 并返回为一个List或set
public void test8(){
List<Emp> emps = empStream.filter(e->e.getSalary()>9000).collect(Collectors.toList());
System.out.println(emps);
}
@Test
public void test9(){
Set<Emp> empSet = empStream.filter(e->e.getSalary()>9000).collect(Collectors.toSet());
empSet.forEach(System.out::println);
}
Optional
Class Optional<T>
Optional是一个容器类,它可以保存类型为T的值,则代表这个值存在,或者仅仅保存为null 表示这个值不存在,原来用null表示一个值不存在,现在Optional可以更好的来表达这个概念,并且还可以避免空指针异常的出现
Optional的常用方法:
创建:
返回值类型 | 方法 |
---|---|
static Optional | of(T value) 返回具有 Optional的当前非空值的Optional。 |
static Optional | empty() 返回一个空的 Optional实例。 |
static Optional | ofNullable(T value) 返回一个 Optional指定值的Optional,如果非空,则返回一个空的 Optional 。 |
判断Optional容器中是否包含对象 :
返回值类型 | 方法 |
---|---|
boolean | isPresent() 返回 true如果存在值,否则为 false 。 |
获取对象 :
返回值类型 | 方法 |
---|---|
T | get() 如果 Optional中有一个值,返回值,否则抛出 NoSuchElementException 。 |
T | orElse(T other) 返回值如果存在,否则返回 other 。 |
T | orElse(T other) 返回值如果存在,否则返回 other 。 |
T | orElseGet(Supplier<? extends T> other) 返回值如果存在,否则返回 other 。 |
使用:
public class OptionalDemo1 {
@Test
public void test1(){
Emp e1 = new Emp(1001,"马云",50,12000,new Dept(10,"总裁办"));
Emp e4 = new Emp(1001,"李彦宏",40,15000,null);
Optional<Dept> opt = Optional.ofNullable(e1.getDept());
// 如果有部门信息就打印,没有则不打印
opt.ifPresent(System.out::println);
Optional<Dept> opt1 = Optional.ofNullable(e4.getDept());
opt1.ifPresent(System.out::println);
}
@Test
public void test2(){
Emp e1 = new Emp(1001,"马云",50,12000,new Dept(10,"总裁办"));
Emp e4 = new Emp(1001,"李彦宏",40,15000,null);
Optional<Dept> opt = Optional.ofNullable(e1.getDept());
Dept dept = opt.orElse(new Dept(50,"秘书处"));
// 如果有部门信息就打印,没有则不打印
opt.ifPresent(System.out::println);
Optional<Dept> opt1 = Optional.ofNullable(e4.getDept());
Dept dept1 = opt1.orElse(new Dept(50,"秘书处"));
System.out.println(dept1);
}
@Test
public void test3(){
Emp e1 = new Emp(1001,"马云",50,12000,new Dept(10,"总裁办"));
Emp e4 = new Emp(1001,"李彦宏",40,15000,null);
Optional<Emp> opt = Optional.ofNullable(e4);
// 判断Optinal中Emp对象是否满足条件,如果满足则保留并打印这个对象 否则就返回空
Optional<Emp> empOpt= opt.filter(e->e.getSalary() > 12000);
System.out.println(empOpt);
}
@Test
public void test4(){
Emp e1 = new Emp(1001,"马云",50,12000,new Dept(10,"总裁办"));
Emp e4 = new Emp(1001,"李彦宏",40,15000,null);
Optional<Emp> opt = Optional.ofNullable(e1);
// 如果Optional中的Emp对象 不为空,就为其翻倍
Optional<Emp> empOptional = opt.map(e->{
e.setSalary(e.getSalary()*2);
return e;
});
System.out.println(empOptional);
}
}
类加载器
类加载【理解】
类加载的描述:
- 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
类的加载:
- 就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
- 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
- 在该阶段,主要就是对类变量进行初始化
类的初始化步骤
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
- 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
类的初始化时机
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
类加载器【理解】
类加载器的作用:
负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象。虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行!
JVM的类加载机制:
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
Java中的内置类加载器:
- Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null
- Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的JavaSE平台API,其实现类和JDK特定的运行时类
- System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
- 类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap
ClassLoader 中的两个方法:
方法名 | 说明 |
---|---|
static ClassLoader getSystemClassLoader() | 返回用于委派的系统类加载器 |
ClassLoader getParent() | 返回父类加载器进行委派 |
public static void main(String[] args) {
ClassLoader loader = ClassLoader.getSystemClassLoader();
System.out.println(loader);//AppClassLoader
ClassLoader loader1 = loader.getParent();
System.out.println(loader1);//PlatformClassLoader
ClassLoader loader2 = loader1.getParent();
System.out.println(loader2);//NULL Bootstrap class loader
}
类加载器:
BootstrapClassLoader 称为引导类加载器,用C++语言编写,是jvm自带的类加载器,负责Java平台的核心库,用来加载核心类库,该类加载器无法直接获取, 获取的时候返回的是null
Extension ClassLoader 负责jre/lib/ext目录下的类的加载
System ClassLoader 负责自定义的类,引入的jar包的相关类的加载 是使用最多的一个加载器