Stream API

Stream API执行流程

Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,可以极大地提高程序员生产力,让程序员写出高效、简洁的代码

实际开发中项目中多数数据源都是来自MySQL、Oracle等关系型数据库,还有部分来自MongDB、Redis等非关系型数据库

  • 当我们从关系型数据库中查询数据时,可以先使用SQL语句在数据库中就对数据进行过滤,排序等操作,最后后台Java程序接收
  • 当我们从非关系型数据库中查询数据时,由于不能在数据库中使用SQL语句操作数据,此时只能靠后台Java程序(Stream API)操作数据库中查询到的数据

Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合/数组进行的操作(可是是非常复杂的查找、过滤和映射数据等操作)

  • 使用Stream API对内存数据(集合/数组)进行操作就类似于使用SQL语句对数据库表中数据的操作,即Stream API提供了一种高效且易于使用的处理数据的方式

Stream和Collection集合的区别: Stream关注的是数据的运算(过滤操作)与CPU打交道, 集合关注的是数据的存储(静态的存储结构)与内存打交道

Stream的特性

  • Stream自己不会存储元素
  • Stream不会改变源对象,相反他们会返回一个持有结果的新Stream
  • Stream操作是延迟执行的,只有执行了终止操作后才会执行中间操作链并产生结果
  • Stream一旦执行了终止操作,就不能再次使用该Stream执行其它中间操作或终止操作了,得重新创建一个新的流才行

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据准备

public class Employee {private int id;private String name;private int age;private double salary;public int getId() {return id;}public void setId(int id) {this.id = id;}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 double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public Employee() {System.out.println("Employee().....");}public Employee(int id) {this.id = id;System.out.println("Employee(int id).....");}public Employee(int id, String name) {this.id = id;this.name = name;}public Employee(int id, String name, int age, double salary) {this.id = id;this.name = name;this.age = age;this.salary = salary;}@Overridepublic String toString() {return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';}@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || getClass() != o.getClass())return false;Employee employee = (Employee) o;if (id != employee.id)return false;if (age != employee.age)return false;if (Double.compare(employee.salary, salary) != 0)return false;return name != null ? name.equals(employee.name) : employee.name == null;}@Overridepublic int hashCode() {int result;long temp;result = id;result = 31 * result + (name != null ? name.hashCode() : 0);result = 31 * result + age;temp = Double.doubleToLongBits(salary);result = 31 * result + (int) (temp ^ (temp >>> 32));return result;}
}

提供用于测试数据的类

public class EmployeeData {public static List<Employee> getEmployees() {List<Employee> list = new ArrayList<>();list.add(new Employee(1001, "马化腾", 34, 6000.38));list.add(new Employee(1002, "马云", 12, 9876.12));list.add(new Employee(1003, "刘强东", 33, 3000.82));list.add(new Employee(1004, "雷军", 26, 7657.37));list.add(new Employee(1005, "李彦宏", 65, 5555.32));list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));list.add(new Employee(1007, "任正非", 26, 4333.32));list.add(new Employee(1008, "扎克伯格", 35, 2500.32));return list;}
}

通过一个数据源创建Stream

方式一通过集合:Java8中的Collection接口扩展了两个获取Stream流的方法

方法名功能
default Stream stream()返回一个顺序流(按照顺序操作元素)
default Stream parallelStream()返回一个并行流(并发的操作元素)
@Test
public void test01(){List<Integer> list = Arrays.asList(1,2,3,4,5);// 返回一个顺序流Stream<Integer> stream1 = list.stream();// 返回一个并行流Stream<Integer> stream2 = list.parallelStream();List<Employee> employees = EmployeeData.getEmployees();// 返回一个顺序流Stream<Employee> stream = employees.stream();// 返回一个并行流Stream<Employee> employeeStream = employees.parallelStream();
}

方式二通过数组: Java8中的Arrays的静态方法stream()可以获取对应类型的Stream流

方法名功能
static Stream stream(T[] array)返回一个自定义类型的Stream流
public static IntStream stream(int[] array)返回一个int类型的Stream流
public static LongStream stream(long[] array)返回一个long类型的Stream流
public static DoubleStream stream(double[] array)返回一个double类型的Stream流
@Test
public void test(){// 获取基本数据类型的Stream流int[] arr = {1,2,3,4,5};IntStream stream = Arrays.stream(arr);// 获取引用数据类型的Stram流String[] arr = {"hello","world"};Stream<String> stream = Arrays.stream(arr); // 获取自定义类型的Stream流Employee kyle = new Employee(9527, "Kyle");Employee lucy = new Employee(9421, "Lucy");Employee[] employees = {kyle, lucy};Stream<Employee> stream1 = Arrays.stream(employees);} 

方式三通过指定具体数据: 调用Stream类静态方法of()创建一个流

方法名功能
public static Stream of(T… values)通过手动指定任意个数据创建一个流
@Test
public void test04(){Stream<Integer> stream = Stream.of(1,2,3,4,5);stream.forEach(System.out::println);
}

方式四调用Stream类的静态方法iterate()和generate()创建无限流(了解)

方法名功能
public static Stream iterate(final T seed, final UnaryOperator f)迭代
public static Stream generate(Supplier s)生成
@Test
public void test() {// 从0开始迭代遍历10个数,没有limit限制会无限迭代Stream.iterate(0, t -> t + 1).limit(10).forEach(System.out::println);// 生成10个随机数,没有limit限制会无限生成Stream.generate(Math::random).limit(10).forEach(System.out::println);
}

中间操作筛选与切片

中间操作就是处理数据的具体操作,每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象

  • 中间操作可以是个操作链可对数据源的数据进行n次处理(多个中间操作),但是所有的中间操作只有在执行终止操作时才会一次性全部处理(惰性求值)
  • Stream流一旦执行了终止操作,就不能再次使用该Stream执行其它中间操作,需要根据数据源重新创建一个新的Stream流
方 法描 述
filter(Predicatep)接收Lambda表达式然后从流中排除某些元素
distinct()筛选通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize)截断流使其元素不超过给定数量
skip(long n)跳过元素返回一个扔掉了前 n个元素的流
若流中元素不足n个则返回一个空流,与 limit(n) 互补
@Test
public void test() {List<Employee> employees = EmployeeData.getEmployees();//1. 查询工资大于7000的员工信息employees.stream().filter(employee -> employee.getSalary() > 7000).forEach(System.out::println);System.out.println("----------------------------");//2. 只输出3条员工信息employees.stream().limit(3).forEach(System.out::println);System.out.println("----------------------------");//3. 跳过前3个元素employees.stream().skip(3).forEach(System.out::println);System.out.println("----------------------------");//4. 通过流所生成元素的hashCode和equals方法去除重复元素employees.add(new Employee(9527, "Kyle", 20, 9999));employees.add(new Employee(9527, "Kyle", 20, 9999));employees.stream().distinct().forEach(System.out::println);
}
/*
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
----------------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
----------------------------
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
----------------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=9527, name='Kyle', age=20, salary=9999.0}
*/

中间操作映射新的Stream

方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上并将其映射成一个新的元素(将元素转换成其他形式或提取信息)
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 DoubleStream
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 IntStream
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 LongStream
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流然后把所有流连接成一个流,

map和flatMap的区别: map方法最终会得到一个含有多个Stream实例的Stream,flatMap最终会得到一个含有多个元素的Stream

@Test
public void test() {ArrayList list1 = new ArrayList();list1.add(1);list1.add(2);list1.add(3);ArrayList list2 = new ArrayList();list2.add(1);list2.add(2);list2.add(3);// 集合中有4个元素,[1,2,3,[4,5,6]],类似maplist1.add(list2);// 集合中有6个元素,[1,2,3,4,5,6],类似flatMaplist1.addAll(list2);System.out.println(list1);
}
public class LambdaTest{@Testpublic void test() {List<String> strings = Arrays.asList("aa", "bb", "cc", "dd");List<Employee> employees = EmployeeData.getEmployees();// 将集合所有元素转为大写并输出strings.stream().map(s -> s.toUpperCase()).forEach(System.out::println);System.out.println("--------------------------");// 获取员工姓名长度大于3的员工的姓名employees.stream().map(Employee::getName).// 过滤出name长度>3的员工filter(name -> name.length() > 3).// 遍历输出forEach(System.out::println);System.out.println("--------------------------");// 使用map将字符串中的多个字符构成的集合转换为对应的Stream实例,然后再获取每一个Stream中的元素strings.stream().map(LambdaTest::formStringToStream).forEach(characterStream -> characterStream.forEach(System.out::println));// 使用flatMap可以直接获取Stream中Stream实例中的元素System.out.println("--------------------------");strings.stream().flatMap(LambdaTest::formStringToStream).forEach(System.out::println);}public static Stream<Character> formStringToStream(String str) {ArrayList<Character> list = new ArrayList<>();for (char c : str.toCharArray()) {list.add(c);}return list.stream();}}
/*
AA
BB
CC
DD
--------------------------
比尔盖茨
扎克伯格
--------------------------
a
a
b
b
c
c
d
d
--------------------------
*/

中间操作排序

方法描述
sorted()产生一个新流其中按自然顺序排序
sorted(Comparator com)产生一个新流其中按比较器顺序排序
@Test
public void test20() {List<Integer> nums = Arrays.asList(13, 54, 97, 52, 43, 64, 27);List<Employee> employees = EmployeeData.getEmployees();//自然排序,如果要对employees自然排序就需要Employee类实现Comparable接口nums.stream().sorted().forEach(System.out::println);//定制排序,先按照年龄升序排,再按照工资降序排employees.stream().sorted((o1, o2) -> {int compare = Integer.compare(o1.getAge(), o2.getAge());if (compare != 0) {return compare;} else {return -Double.compare(o1.getSalary(), o2.getSalary());} }).forEach(System.out::println);
}/*
13
27
43
52
54
64
97
------------------
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
*/

终止操作匹配与查找

终端操作会从流的流水线生成结果,方法返回值类型就不再是Stream了可以是任何不是流的值(如List,Integer,void等)

  • 流进行了终止操作后不能再次使用执行其他中间操作或终止操作
方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素,顺序流会取第一个元素
count()返回流中元素总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值
forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。
相反,Stream API 使用内部迭代——它帮你把迭代做了)
@Test
public void test21(){List<Employee> employees = EmployeeData.getEmployees();// 练习:是否所有的员工的工资是否都大于5000System.out.println("是否所有的员工的工资是否都大于5000:"+employees.stream().allMatch(employee -> employee.getSalary() > 5000));// 练习:是否存在员工年龄小于15System.out.println("是否存在员工年龄小于15:"+employees.stream().anyMatch(employee -> employee.getAge() < 15));// 练习:是否不存在员工姓“马”System.out.println("是否不存在员工姓马:"+employees.stream().noneMatch(employee -> employee.getName().startsWith("马")));// 返回流中的第一个元素System.out.println("返回第一个元素:"+employees.stream().findFirst());// 返回当前流中的任意元素System.out.println("返回当前流中的任意元素"+employees.stream().findAny());// 返回流中元素的总个数(返回总个数前可以先过滤)System.out.println("返回元素总数:"+employees.stream().count());// 返回流中最高工资System.out.println("返回最高工资:"+employees.stream().map(Employee::getSalary).max(Double::compare));// 返回最低工资的员工System.out.println("返回最高工资:"+employees.stream().min(e1,e2 -> Double.compare(e1.getSalary(),e2.getSalary()));// 返回流中最小值System.out.println("返回最小年龄:"+employees.stream().map(Employee::getAge).min(Integer::compare));// 内部迭代employees.stream().forEach(System.out::println);System.out.println("-------------");// 使用集合的遍历操作employees.forEach(System.out::println);
}/*
是否所有的员工的工资是否都大于5000:false
是否存在员工年龄小于15:true
是否不存在员工姓马:false
返回第一个元素:Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
返回当前流中的任意元素Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
返回元素总数:8
返回最高工资:Optional[9876.12]
返回最小年龄:Optional[12]
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
-------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
*/    

终止操作归约

map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名

方法描述
reduce(T identity, BinaryOperator b)可以将流中元素反复结合起来返回一个值T
reduce(BinaryOperator b)可以将流中元素反复结合起来返回一个值Optional
@Test
public void test22() {List<Integer> nums = Arrays.asList(13, 32, 23, 31, 94, 20, 77, 21, 17);List<Employee> employees = EmployeeData.getEmployees();// 练习1:计算1-10的自然数的和(0是初始值)System.out.println(nums.stream().reduce(0, Integer::sum));// 练习2:手动计算公司所有员工工资总和System.out.println(employees.stream().map(Employee::getSalary).reduce((o1, o2) -> o1 + o2));// 调用Integer的sum方法计算年龄总和System.out.println(employees.stream().map(Employee::getAge).reduce(Integer::sum));// 计算公司所有员工工资综合Stream<Double> salaryStream = employ.stream.map(Employee::getSalary);Optional<Double> sumMoney = salaryStream.reduce(Double::sum);System.out.println(sumMoney);
}
/*
328
Optional[48424.08]
Optional[273]
Optional[48424.08]
*/

终止操作收集

方 法描 述
collect(Collector c)接收一个Collector接口的实现将流转换为其他形式,用于给Stream中元素做汇总的方法

Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map),可以方便地创建常见收集器实例

方法返回类型作用
toListList把流中元素收集到List
List emps= list.stream().collect(Collectors.toList());
toSetList把流中元素收集到List
Set emps= list.stream().collect(Collectors.toSet());
toCollectionCollection把流中元素收集到创建的集合
Collection emps =list.stream().collect(Collectors.toCollection(ArrayList::new));
countingLong计算流中元素的个数
long count = list.stream().collect(Collectors.counting());
summinglntInteger对流中元素的整数属性求和
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingIntDouble计算流中元素Integer属性的平均值
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizinglntIntSummaryStatistics收集流中Integer属性的统计值。如:平均值
int SummaryStatisticsiss=list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joiningString连接流中每个字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxByOptional根据比较器选择最大值
optionalmax=list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary));
minByOptional根据比较器选择最小值
Optionalmin = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary));
reducing归约产生的类型从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
int total=list.stream().collect(Collectors.reducing(0, Employe::getSalar, Integer::sum));
collectingAndThen转换函数返回的类型包裹另一个收集器,对其结果转换函数
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingByMap<K, List>根据某属性值对流分组,属性为K,结果为V
Map<Emp.Status, List>map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
partitioningByMap<Boolean,List>根据true或false进行分区
Map<Boolean,List>vd=list.stream().collect(Collectors.partitioningBy(Employee::getManage));
@Test
public void test23() {// 练习1:查找工资大于6000的员工,结果返回为一个ListList<Employee> employees = EmployeeData.getEmployees();List<Employee> list = employees.stream().filter(employee -> employee.getSalary() > 6000).collect(Collectors.toList());list.forEach(System.out::println);System.out.println("--------------------");// 练习2:查找年龄大于20的员工,结果返回为一个Listemployees.add(new Employee(9527,"Kyle",21,9999));employees.add(new Employee(9527,"Kyle",21,9999));Set<Employee> set = employees.stream().filter(employee -> employee.getAge() > 20).collect(Collectors.toSet());set.forEach(System.out::println);
}/*
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
--------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=9527, name='Kyle', age=21, salary=9999.0}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
*/

Optional类

空指针异常是导致Java应用程序失败的最常见原因,以前为了解决空指针异常Google公司著名的Guava项目引入了Optional类(通过检查空值的方式来防止代码污染)

受到Google Guava的启发Java 8也引入了java.util.Optional类,Optional类是一个可以为null的容器对象

  • Optional类可以保存类型T的值代表这个值存在
  • 以前用null表示一个值不存在,现在Optional类保存null表示这个值不存在可以避免空指针异常

创建Optional类

创建Optional类对象的方法

方法名功能
Optional.of(T t)创建一个Optional实例,t必须非空
Optional.empty()创建一个空的Optional实例
Optional.ofNullable(T t)t可以为null

准备实体类Boy和Girl

public class Boy {private Girl girl;public Boy() {}public Boy(Girl girl) {this.girl = girl;}@Overridepublic String toString() {return "Boy{" +"girl=" + girl +'}';}
}
public class Girl {private String name;public Girl() {}public Girl(String name) {this.name = name;}@Overridepublic String toString() {return "Girl{" +"name='" + name + '\'' +'}';}
}
@Test
public void test(){Girl girl = new Girl();// 如果girl等于null会报空指针异常Optional<Girl> optionalGirl = Optional.of(girl);
}@Test
public void test25(){Girl girl = new Girl();girl = null;// girl可以为nullOptional<Girl> optionalGirl = Optional.ofNullable(girl);// girl为null输出Optional.empty,girl不为null输出Optional[Girl{name="null"}]System.out.println(optionalGirl);// girl为null输出Girl{name="赵丽颖"},girl不为null输出Girl{name="null"}Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖"));System.out.println(girl1);
}

使用Optional

判断Optional容器中是否包含对象

方法名功能
boolean isPresent()判断是否包含对象
void ifPresent(Consumer<? super T> consumer)如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它

获取Optional容器的对象

方法名功能
T get()如果调用对象包含值返回该值,否则抛异常
T orElse(T other)如果有值(内部封装的t非空)则将其返回,否则返回指定的other对象
T orElseGet(Supplier<? extends T> other)如果有值则将其返回,否则返回由Supplier接口实现提供的对象
T orElseThrow(Supplier<? extends X> exceptionSupplier)如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
@Test
public void test26(){Boy boy = new Boy();boy = null;// 此会出现空指针异常String girlName = getGirlName(boy);System.out.println(girlName);
}// 获取女孩的名字,容易出现空指针异常
private String getGirlName(Boy boy) {return boy.getGirl().getName();
}
@Test
public void test27(){Boy boy = new Boy();boy = null;String girlName = getGirlName1(boy);System.out.println(girlName);
}// 优化以后的getGirlName():
public String getGirlName1(Boy boy){if(boy != null){Girl girl = boy.getGirl();if(girl != null){return girl.getName();}}return null;
}

使用Optional类

@Test
public void test28(){// 樱岛麻衣Boy boy = null;// 喜多川海梦boy = new Boy();// Lucyboy = new Boy(new Girl("Lucy"));String girlName = getGirlName2(boy);System.out.println(girlName);
}//使用Optional类的getGirlName()
public String getGirlName2(Boy boy){Optional<Boy> boyOptional = Optional.ofNullable(boy);//此时的boy1一定非空Boy boy1 = boyOptional.orElse(new Boy(new Girl("樱岛麻衣")));Girl girl = boy1.getGirl();Optional<Girl> girlOptional = Optional.ofNullable(girl);//girl1一定非空Girl girl1 = girlOptional.orElse(new Girl("喜多川海梦"));return girl1.getName();
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/125067.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3路由跳转params传参接收不到

import { useRouter } from "vue-router";const router useRouter(); // 提现记录 const withdrawalClick (item) > {router.push({ name: "Devwithdrawal", params: { name: 123 } }); };//跳转页面接收参数 import { useRoute } from "vue-rou…

网络是如何进行通信

网络是如何进行通信的 简介 在现代社会中&#xff0c;网络已经成为我们生活中不可或缺的一部分。从上网搜索信息、在线购物到远程工作和社交媒体&#xff0c;我们几乎无时无刻不与网络保持着联系。但是&#xff0c;网络究竟是个什么玩意&#xff0c;它是如何工作的呢&#xf…

Web服务器简介及HTTP协议

一、Web Server&#xff08;网页服务器&#xff09; 一个 Web Server 就是一个服务器软件&#xff08;程序&#xff09;&#xff0c;或者是运行这个服务器软件的硬件&#xff08;计算机&#xff09;。其主要功能是通过 HTTP 协议与客户端&#xff08;通常是浏览器&#xff08…

python可视化模块—快速利用matplot绘制图表

文章目录 一、Matplotlib基本介绍二、两种绘图方式区别&#xff08;plt.*** 和ax.***&#xff09;三、如何使用Matplotlib绘图1、画布—绘画的画板2、配置—更个性化的绘图全局配置局部配置面向对象绘图过程&#xff1a;ax代表子图变量过程式绘图过程 四、常用绘图图形如何选择…

配电房能源监测系统

配电房能源监测系统是一种能够实时监测和管理配电房能源消耗的系统&#xff0c;有助于企业更好地管理能源使用&#xff0c;降低能源成本&#xff0c;提高能源利用效率。本文将详细介绍配电房能源监测系统的组成、功能和优点。 一、配电房能源监测系统的组成 配电房能源监测系统…

网络通信深入解析:探索TCP/IP模型

http协议访问web 你知道在我们的网页浏览器的地址当中输入url&#xff0c;未必是如何呈现的吗&#xff1f; web浏览器根据地址栏中指定的url&#xff0c;从web服务器获取文件资源&#xff08;resource&#xff09;等信息&#xff0c;从而显示出web页面。web使用HTTP&#xff08…

OpenHarmony 使用 ArkUI Inspector 分析布局

● 摘要&#xff1a;视图的嵌套层次会影响应用的性能&#xff0c;开发者应该移除多余的嵌套层次&#xff0c;缩短组件刷新耗时。本文会介绍如何使用 ArkUI Inspector 工具分析布局&#xff0c;提示应用响应性能。 ● 关键字&#xff1a;列举本文相关的关键字&#xff1a;OpenH…

vue3:3、项目目录和关键文件

关于vsvode的更改 <!-- 加上setup允许在script中直接编写组合式api --> <script setup> // 组件引入后直接用 import HelloWorld from ./components/HelloWorld.vue import TheWelcome from ./components/TheWelcome.vue</script><!-- 1、js放在最上面&am…

Mavan进阶之多模块(聚合)

文章目录 Maven 多模块&#xff08;聚合&#xff09;非父子关系的多模块项目 Maven 多模块&#xff08;聚合&#xff09; Maven 继承和聚合是 2 个独立的概念。工程与工程之间可能毫无关系&#xff0c;也可能是继承关系&#xff0c;也可能是聚合关系&#xff0c;也可能既是继承…

美客多选品趋势分析,美客多选品时的注意事项

都知道选品的重要性&#xff0c;美客多这个平台也一样&#xff0c;选品选对了肯定事半功倍&#xff0c;本文介绍了美客多选品趋势分析&#xff0c;美客多选品时的注意事项&#xff0c;一起来了解下吧。、 美客多选品趋势分析 1、墨西哥站&#xff1a;跨境支付高&#xff0c;偏…

CSS---flex布局

主要记录flex布局的要点以及实例 flex flex父标签的6个属性flex-direction: flex布局的方向flex-wrap: 是否可以换行flex-flow: flex-direction 和 flex-wrap 一起写justify-content&#xff1a;横向对齐方式align-items: 纵向对齐方式align-content: 有换行情况下的纵向对齐方…

【MySQL系列】MySQL的事务管理的学习(一)_ 事务概念 | 事务操作方式 | 事务隔离级别

「前言」文章内容大致是MySQL事务管理。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、事务概念二、事务的版本支持三、事务提交方式四、事务常见的操作方式4.1 事务正常操作4.2 事务异常验证 五、事务隔离级别5.1 查看与设置隔离性5.2 读未提交&…

jmeter 准确的吞吐量定时器 Precise Throughput Timer

准确的吞吐量定时器使用实例 提取码&#xff1a;gpex&#xff1a; 说明&#xff1a;配置10个线程&#xff0c;每个线程请求200次&#xff0c;通过准确地的定时器模拟QPS为20的场景 配置测试接口参考链接 配置jmeter测试脚本&#xff0c;主要关注准确的吞吐量定时器参数配置 目…

OpenCV(二十六):边缘检测(二)

目录 1.Laplacian算子边缘检测 原理&#xff1a; Laplacian边缘检测函数Laplacian() 示例代码&#xff1a; 2.Canny算子边缘检测 原理&#xff1a; Canny算法函数Canny() 示例代码&#xff1a; 1.Laplacian算子边缘检测 原理&#xff1a; Laplacian算子的原理基于图像…

uniapp分包 解决分多个包的问题

1. 分包可以分很多个, 但是在"optimization": { "subPackages": true } 里面只能写一个, 2. 想分多个包 , 在 pages.json 里面 的 subPackages 里面继续加 第三个 第四个即可 3. 保存之后 创建页面就可以看见多个包了

固定资产台账怎么管理

固定资产台账是指企业对固定资产进行登记、分类、统计和管理的账簿。云呐固定资产管理系统是一款专业的固定资产管理软件&#xff0c;可以帮助企业实现资产全生命周期管理&#xff0c;包括资产采购、入库、领用、归还、维修、报废等环节。系统具有实时监控、预警提醒、报表分析…

java+ssm+mysql水费管理系统

项目介绍&#xff1a; 使用javassmmysql开发的用户水费管理系统&#xff0c;系统包含超级管理员&#xff0c;系统管理员、用户角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理、用户管理、用水管理&#xff08;用水记录、缴费提醒&#xff09;、水费…

06 科技英语|控制与优化学科词汇

maneuver n 策略&#xff1b;v 操控、调遣 manipulate vt 熟练控制 scalability n 可扩展性 leverage n 杠杆&#xff1b;v 促使...改变 flexibility n 弹性 dispatch n 急件&#xff1b;v 调度&#xff1b;派遣 leverage …

流程图用什么软件做比较好?这几个实用软件了解下

流程图用什么软件做比较好&#xff1f;流程图的制作对于企业管理、项目管理、产品设计等领域都非常重要。制作流程图可以帮助我们更好地理解事物之间的关系和流程&#xff0c;从而规划和组织工作。因此&#xff0c;选择一个合适的流程图软件是非常必要的。下面就给大家介绍几种…

网络层重点协议-IP协议(结构分析)

IP协议数据报格式 一.4位版本号 用来表示IP协议的版本&#xff0c;现有的IP协议只有两个版本IPv4和IPv6 二.4位首部长度 IP协议数据报报头的长度 三.8位服务类型 3位优先权字段&#xff08;已经弃用&#xff09;&#xff0c;4位TOS字段&#xff0c;和1位保留 字段&#xff08;必…