19 – 20. Java学习 – JDK1.8新特性
1. lambda表达式
lambda表达式,是jdk1.8的新特性,其实就是匿名函数的另一种写法,目的是用来简化代码。lambda表达式的使用前提是必须存在函数式接口。
所谓的函数式接口,就是一个接口里面只能有一个抽象方法。java官方也定义了很多内置的函数式接口,这些函数式接口使用 @FunctionalInterface 注解修饰。
自己定义函数式接口时, @FunctionalInterface 注解可以写,也可以不写。标注 @FunctionalInterface 注解会自动约束仅让在接口中声明一个抽象方法,多的会报错。
1.1. 不带参数和返回值
lambda表达式语法格式:
() -> {
函数体...
}
例子:lambda表达式的基本使用
// 定义函数式接口
@FunctionalInterface
interface PersonDao {
public void show();
}
// 测试
@Test
public void test01() {
PersonDao per = () -> {
System.out.println("使用lambda表达式实现");
};
per.show();
}
1.2. 带参数和返回值
带参数的lambda表达式中,( )中的参数类型可以不写,lambda表达式会自动匹配。
lambda表达式语法格式:
(参数列表) ->{
// 函数体
return 结果;
}
例子:
// 定义函数式接口
@FunctionalInterface
interface NumDao {
public int show(int num);
}
// 测试
@Test
public void test02() {
NumDao num = (num1) -> {
return num1 * 10;
};
System.out.println(num.show(10)); // 100
}
以上代码还可以继续简化。
当参数只有一个,函数体代码有且仅有一行,那么前面的 ( ) 和后面的 { } 以及 return关键字 都可以被省略。
// 定义函数式接口
@FunctionalInterface
interface NumDao {
public int show(int num);
}
// 测试
@Test
public void test03() {
NumDao num = num1 -> num1 * 10;
System.out.println(num.show(20));
}
当有多个参数时,有几个参数括号中就写几个参数。
// 定义函数式接口
interface AvgDao {
public double show(int num1, int num2);
}
// 测试
@Test
public void test04() {
AvgDao avg = (num1, num2) -> (num1 + num2) / 2;
System.out.println(avg.show(10, 20)); // 15.0
}
Lambda表达式的使用总结:
- lambdabaioda前提:必须存在函数式接口,只能对函数式接口中的方法进行实现。要求接口中有且只有一个抽象方法
- 在定义lambda表达式的时候,定义参数的时候,参数列表中的参数类型可以省略,因为编译器可以自行推断参数的数据类型
- 如果参数列表中的参数有且只有一个,那么 圆括号( ) 是可以省略的
- 如果lambda表达式的函数体中的代码有且只有一行,那么 花括号{ } 也是可以省略的
- 如果lambda表达式的函数体中的代码有且只有一行代码,代码是使用return关键字返回数据,此时return关键字也可以省略
2. 函数式接口
函数式接口就是只能定义一个抽象方法的接口。一般使用注解@FunctionalInterface注解修饰。
在java中,官方提供了很多内置的函数式接口。比如Runnable接口,Comparator接口,都属于函数式接口。
2.1. 内置函数式接口的介绍
在java的java.util.function包下面,定义了很多内置的函数式接口
- 消费型函数式接口
特点:接收传递的参数,但是不返回任何数据
// 消费型函数式接口
@FunctionalInterface
public interface Consumer<T> {
/**
* 接收传递的参数,但是不返回任何数据
*/
void accept(T t);
}
- 供给型函数式接口
特点:不传递任何参数,方法的返回值类型就是泛型接口中定义的数据类型
// 供给型函数式接口
@FunctionalInterface
public interface Supplier<T> {
/**
* 不传递任何参数,方法的返回值类型就是泛型接口中定义的数据类型
*/
T get();
}
- 函数型函数式接口
特点:传递的参数为T,返回值为R
// 函数型函数式接口
@FunctionalInterface
public interface Function<T, R> {
/**
* 传递的参数为T,返回值为R
*/
R apply(T t);
}
- 断言型函数式接口
特点:判断传递的参数T是否满足指定的约束或者规范,满足返回true,不满足返回false
// 断言型函数式接口
@FunctionalInterface
public interface Predicate<T> {
/**
* 判断传递的参数T是否满足指定的约束或者规范,满足返回true,不满足返回false。
*/
boolean test(T t);
}
2.2. 消费型函数式接口的使用
特点:接收传递的参数,但是不返回任何数据
public class ConsumerFunctionDemo {
public void getResult(double money, Consumer<Double> consumer) {
consumer.accept(money);
}
// 原来的写法
@Test
public void beforeTest() {
getResult(22.34, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("金额是: " + aDouble); // 金额是: 22.34
}
});
}
// 基于lambda表达式的写法
@Test
public void newTest() {
getResult(8.88, money -> System.out.println("金额是: " + money)); // 金额是: 8.88
}
}
2.3. 供给型函数式接口
特点:不传递任何参数,方法的返回值类型就是泛型接口中定义的数据类型
public class SupplierFunctionDemo {
public String generateValidateCode(Supplier<String> supplier) {
return supplier.get();
}
// 原来的写法
@Test
public void beforeTest() {
String s = generateValidateCode(new Supplier<String>() {
@Override
public String get() {
return RandomUtil.randomNumbers(6);
}
});
System.out.println("验证码是: " + s); // 验证码是: 805808
}
// lambda表达式的写法
@Test
public void newTest() {
String s = generateValidateCode(() -> RandomUtil.randomNumbers(6));
System.out.println("验证码是: " + s); // 验证码是: 196504
}
}
以上例子中,用到了一个很有用的工具库 – hutool-all,想使用这个库需要在Maven中引入依赖:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.0</version>
</dependency>
hutool-all库包含了许多功能模块:
-
字符串处理: 提供了丰富的字符串处理方法,包括字符串切割、连接、格式化、Unicode转换等
-
日期时间处理: 提供了日期时间的格式化、解析、计算、时区转换等功能
-
加密解密: 支持常见的加密算法,如MD5、SHA等,也包括AES、RSA等对称和非对称加密解密方法
-
文件操作: 提供了文件读写、复制、移动、文件类型判断等操作的工具方法
-
网络通信: 提供了HTTP客户端、服务器等网络通信相关的工具类,简化了HTTP请求的发送和处理
-
图片处理: 包含了图片缩放、水印添加、图片格式转换等图片处理功能
比如以上例子中就使用了hutool-all库来生成随机验证码。
2.4. 函数型函数式接口
特点:传递的参数为T,返回值为R
public class FunctionDemo {
public Integer getLength(String message, Function<String, Integer> function) {
return function.apply(message);
}
// 原来的写法
@Test
public void beforeTest() {
Integer length = getLength("HelloWorld", new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
});
System.out.println("字符串长度为: " + length); // 字符串长度为: 10
}
// lambda表达式写法
@Test
public void newTest() {
Integer length = getLength("Bye", s -> s.length());
System.out.println("字符串长度为: " + length); // 字符串长度为: 3
}
}
函数式接口,也可以作为方法的返回值:
// 函数式接口做返回值
// 原来的写法
public Function<Integer, String> getMethod() {
return new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return "hello" + integer;
}
};
}
// lambda表达式写法
public Function<Integer, String> newGetMethod() {
return integer -> "hello" + integer;
}
2.5. 断言型函数式接口
特点:判断传递的参数T是否满足指定的约束或者规范,满足返回true,不满足返回false
public class PredicateFunctionDemo {
public List<String> filterList(List<String> list, Predicate<String> pre) {
ArrayList<String> newList = new ArrayList<>();
for (String str : list) {
if (pre.test(str)) {
newList.add(str);
}
}
return newList;
}
public static List<String> list = new ArrayList<>();
static {
list.add("北京");
list.add("上海");
list.add("南京");
list.add("深圳");
list.add("广州");
list.add("广东");
}
// 原来的写法
@Test
public void beforeTest() {
List<String> newList = filterList(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(newList); // [北京, 南京]
}
// lambda表达式写法
@Test
public void newTest() {
List<String> newList = filterList(list, s -> s.contains("广"));
System.out.println(newList); // [广州, 广东]
}
}
3. 方法引用
方法引用是一种简化lambda表达式的方式,也是对lambda表达式的深层次运用。其使编写代码时可以直接引用类的方法,而不需要手写完整的lambda表达式。
**“::”**是方法引用的操作符,方法引用可以使代码更简洁(清晰易读这个优点我觉得有待商榷)。
方法引用需要注意:
- 需要函数式接口
- 被引用的方法必须已经存在
- 被引用方法的形参和返回值需要跟抽象方法保持一致
- 被引用方法的功能需要满足当前的需求
方法引用的4种语法形式:
- 静态方法引用:类名 :: 静态方法名称
- 实例方法引用:对象名 :: 实例方法名称
- 构造方法引用:类名 :: new
- 特定方法引用:类名 :: 方法名
3.1. 体验方法引用
例1:
public class MethodReferenceDemo1 {
// 原来的写法
@Test
public void test01() {
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("hello");
}
// lambda表达式写法
@Test
public void test02() {
Consumer<String> con = s -> System.out.println(s);
con.accept("world");
}
// 方法引用
@Test
public void test03() {
Consumer<String> con = System.out::println;
con.accept("java");
}
}
例2:
@Data
@NoArgsConstructor
@AllArgsConstructor
class Employee {
private Integer id;
private String name;
private Integer age;
private Integer salary;
}
public class MethodReferenceDemo2 {
// 原来的写法
@Test
public void test01() {
Employee aaa = new Employee(101, "AAA", 22, 1000);
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return aaa.getName();
}
};
System.out.println(supplier.get());
}
// lambda表达式写法
@Test
public void test02() {
Employee bbb = new Employee(102, "BBB", 23, 1500);
Supplier<String> supplier = () -> bbb.getName();
System.out.println(supplier.get());
}
// 方法引用
@Test
public void test03() {
Employee ccc = new Employee(102, "CCC", 21, 2000);
Supplier<String> supplier = ccc::getName;
System.out.println(supplier.get());
}
}
3.2. 静态方法引用(类名 :: 静态方法名称)
条件:lambda表达式里面有且只调用了1个静态方法,并且前后参数一致
例1:
public class StaticMethodReferenceDemo1 {
// 原来的写法
@Test
public void test01() {
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2); // 静态方法,比较两个int型数字的值
}
};
System.out.println(comparator.compare(12, 23));
}
// lambda表达式写法
@Test
public void test02() {
Comparator<Integer> comparator = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(comparator.compare(23, 13));
}
// 方法引用
@Test
public void test03() {
Comparator<Integer> comparator = Integer::compare;
System.out.println(comparator.compare(45, 23));
}
}
例2:
public class StaticMethodReferenceDemo2 {
// 原来的写法
@Test
public void test01() {
Function<Double, Long> function = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return Math.round(aDouble); // 将一个数字四舍五入为最接近的整数
}
};
System.out.println(function.apply(3.1459265358));
}
// lambda表达式写法
@Test
public void test02() {
Function<Double, Long> function = d -> Math.round(d);
System.out.println(function.apply(2.23333));
}
// 方法引用
@Test
public void test03() {
Function<Double, Long> function = Math::round;
System.out.println(function.apply(345.21432));
}
}
实践:将Employee数组按薪资排序
public class StaticMethodReferenceDemo3 {
public static Employee[] emp = {new Employee(101, "AAA", 23, 2544),
new Employee(102, "BBB", 21, 2654),
new Employee(103, "CCC", 22, 2457),
new Employee(104, "DDD", 25, 7542),
new Employee(105, "EEE", 24, 2000)};
// 原来的写法
@Test
public void test01() {
Arrays.sort(emp, new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return o1.getSalary() - o2.getSalary();
}
});
for (Employee e : emp) {
System.out.println(e);
}
}
// lambda表达式写法
@Test
public void test02() {
Arrays.sort(emp, (o1, o2) -> o1.getSalary() - o2.getSalary());
for (Employee e : emp) {
System.out.println(e);
}
}
// 方法引用
@Test
public void test03() {
Arrays.sort(emp, EmployeeMethod::compareEmployee);
for (Employee e : emp) {
System.out.println(e);
}
}
}
class EmployeeMethod {
public static int compareEmployee(Employee e1, Employee e2) {
return e1.getSalary() - e2.getSalary();
}
}
3.3. 实例方法引用(对象名 :: 实例方法名称)
条件:lambda表达式里面前后参数是一致,并且方法体里面的方法是通过对象调用
例1:
public class MethodReferenceDemo3 {
public interface UserDao {
public String save(String str1, String str2);
}
// 原来的写法
@Test
public void test01() {
UserMethod userMethod = new UserMethod();
UserDao userDao = new UserDao() {
@Override
public String save(String str1, String str2) {
return userMethod.generateStr(str1, str2);
}
};
System.out.println(userDao.save("hello, ", "world"));
}
// lambda表达式写法
@Test
public void test02() {
UserMethod userMethod = new UserMethod();
UserDao userDao = (s1, s2) -> userMethod.generateStr(s1, s2);
System.out.println(userDao.save("Wow, ", "interesting"));
}
// 方法引用
@Test
public void test03() {
UserMethod userMethod = new UserMethod();
UserDao userDao = userMethod::generateStr;
System.out.println(userDao.save("No, ", "not interesting"));
}
}
class UserMethod {
public String generateStr(String str1, String str2) {
return str1.concat(str2);
}
}
实践:遍历集合中的元素
public class MethodReferenceDemo3 {
public static ArrayList<Employee> emp = new ArrayList<>();
static {
emp.add(new Employee(101, "AAA", 23, 4516));
emp.add(new Employee(102, "BBB", 24, 4599));
emp.add(new Employee(103, "CCC", 21, 6542));
emp.add(new Employee(104, "DDD", 25, 3485));
emp.add(new Employee(105, "EEE", 23, 7846));
emp.add(new Employee(106, "FFF", 24, 2514));
}
// 原来的写法
@Test
public void test04() {
emp.forEach(new Consumer<Employee>() {
@Override
public void accept(Employee employee) {
System.out.println(employee);
}
});
}
// lambda表达式写法
@Test
public void test05() {
emp.forEach(e -> System.out.println(e));
}
// 方法引用
@Test
public void test06() {
emp.forEach(System.out::println);
}
}
3.4. 构造方法引用(类名 :: new)
条件:lambda表达式里面只创建对象,并且前后参数一致
例1:
public class ConstructorReferenceDemo1 {
// 原来的写法
@Test
public void test01() {
Supplier<Employee> aaa = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
Employee employee = aaa.get();
System.out.println(employee);
}
// lambda表达式写法
@Test
public void test02() {
Supplier<Employee> supplier = () -> new Employee();
Employee employee = supplier.get();
System.out.println(employee);
}
// 方法引用
@Test
public void test03() {
Supplier<Employee> supplier = Employee::new;
Employee employee = supplier.get();
System.out.println(employee);
}
}
例2:
public class ConstructorReferenceDemo2 {
// 原来的写法
@Test
public void test01() {
BiFunction<String, Integer, Employee> biFunction = new BiFunction<String, Integer, Employee>() {
@Override
public Employee apply(String s, Integer integer) {
return new Employee(s, integer);
}
};
}
// lambda表达式写法
@Test
public void test02() {
BiFunction<String, Integer, Employee> biFunction = (s, i) -> new Employee(s, i);
}
// 方法引用
@Test
public void test03() {
BiFunction<String, Integer, Employee> biFunction = Employee::new;
}
}
3.5. 特定方法引用(类名 :: 方法名)
条件:lambda表达式里面只有一个实例方法,并且前面参数列表中的第一个参数是主调参数(通过这个参数调用方法),后面的所有参数被当成入参传入到方法里
例1:
public class MethodReferenceDemo4 {
public static String[] str = {"hello", "basketball", "football",
"swimming", "flying", "Fight",
"Zoom", "Hello"};
// 原来的写法
@Test
public void test01() {
// 大写小写分开排序
Arrays.sort(str);
System.out.println(Arrays.toString(str));
// 大小写排序不分开
Arrays.sort(str, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
});
System.out.println(Arrays.toString(str));
}
// lambda表达式写法
@Test
public void test02() {
Arrays.sort(str, (o1, o2) -> o1.compareToIgnoreCase(o2));
System.out.println(Arrays.toString(str));
}
// 方法引用
@Test
public void test03() {
Arrays.sort(str, String::compareToIgnoreCase);
System.out.println(Arrays.toString(str));
}
}
案例:比较两个字符串的内容是否一致,忽略大小写比较
public class MethodReferenceDemo4 {
public static String[] str = {"hello", "basketball", "football",
"swimming", "flying", "Fight",
"Zoom", "Hello"};
// 原来的写法
@Test
public void test04() {
StringDao stringDao = new StringDao() {
@Override
public boolean show(String s1, String s2) {
return s1.equalsIgnoreCase(s2);
}
};
System.out.println(stringDao.show("Hello", "hello"));
}
// lambda表达式写法
@Test
public void test05() {
StringDao stringDao = (s1, s2) -> s1.equalsIgnoreCase(s2);
System.out.println(stringDao.show("world", "World"));
}
// 方法引用
@Test
public void test06() {
StringDao stringDao = String::equalsIgnoreCase;
System.out.println(stringDao.show("WOW", "wow"));
}
}
4. Stream流
Stream流也是JDK1.8的性特性,其提供了非常强大的API帮助我们来进行集合或数组操作。Stream流简化了我们对数组或集合中元素的操作,使我们更专注于做什么(What),而不是怎么做(How)。
如何使用Sream流:
- 创建Stream流。通过集合或数组来创建
- 进行中间操作。一个中间操作由很多小的操作组成,形成一个操作链,这个操作链可以对集合或数组中的元素进行处理。比如,集合或数组元素的去重、过滤等
- 进行终止操作。一旦执行终止操作,就会将中间操作的所有操作链全部执行,并产生最终的结果
4.1. 体验Stream流
例子:在集合中存储一些名字,找出名字中姓王的名字,并且名字的长度是3
public class StreamDemo1 {
public static ArrayList<String> list = new ArrayList<>();
static {
list.add("王上");
list.add("赵佐佑");
list.add("王商夏");
list.add("刘下");
list.add("王流漆");
list.add("李斯乌");
}
public List<String> getNewNames(List<String> list, Predicate<String> predicate) {
List<String> newList = new ArrayList<>();
for (String s : list) {
if (predicate.test(s)) {
newList.add(s);
}
}
return newList;
}
// 原来的方法
@Test
public void test01() {
List<String> newList = getNewNames(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("王") && s.length() == 3;
}
});
System.out.println(newList);
}
// Stream流
@Test
public void test02() {
/*List<String> newList = list.stream().filter(s -> s.startsWith("王") && s.length() == 3).
collect(Collectors.toList());*/
List<String> newList = list.stream().filter(s -> s.startsWith("王")).
filter(s -> s.length() == 3).collect(Collectors.toList());
System.out.println(newList);
}
}
4.2. 创建Stream流
创建Stream流对象的方法:
- 集合对象.stream():基于集合对象创建Stream流对象
- Arrays.stream(数组对象):根据数组对象创建Stream流对象
- Stream.of(T … values):向Stream中添加多个数据
备注:of
方法的参数其实是一个可变参数,所以也支持数组
4.3. 常用方法
方法分为两种:终结方法、非终结方法
终结方法:返回值类型不再是
Stream
接口自身类型的方法,因此不再支持类似StringBuilder
那样的链式调用。本小节中,终结方法包括count
和forEach
方法非终结方法:返回值类型仍然是
Stream
接口自身类型的方法,因此支持链式调用。除了终结方法外,其余方法均为非终结方法
4.3.1. 非终结方法
筛选与切片:
- filter:指定条件对集合中的元素进行过滤
- limit:取出前几个元素
- skip:跳过指定个数的元素
- distinct:对集合中的元素去重
public class StreamDemo2 {
public static List<Employee> list = new ArrayList<>();
static {
list.add(new Employee(101, "AAA", 23, 5489));
list.add(new Employee(102, "BBB", 21, 4587));
list.add(new Employee(103, "CCC", 22, 2513));
list.add(new Employee(104, "DDD", 26, 6489));
list.add(new Employee(105, "EEE", 25, 7000));
list.add(new Employee(106, "FFF", 24, 4050));
}
@Test
public void test01() {
// 创建Stream流对象
Stream<Employee> stream = list.stream();
// 找到薪资大于5000的人
stream.filter(s -> s.getSalary() > 5000).forEach(System.out::println);
System.out.println("======================================");
// 输出所有元素
stream.forEach(System.out::println);
System.out.println("======================================");
//输出前三个元素
stream.limit(3).forEach(System.out::println);
System.out.println("======================================");
// 跳过指定元素个数
stream.skip(4).forEach(System.out::println);
System.out.println("======================================");
// 添加元素去重
list.add(new Employee(105, "EEE", 25, 7000));
list.add(new Employee(106, "FFF", 24, 4050));
list.add(new Employee(105, "EEE", 25, 7000));
list.add(new Employee(106, "FFF", 24, 4050));
// 输出元素
stream.forEach(System.out::println);
System.out.println("======================================");
stream.distinct().forEach(System.out::println);
}
}
映射:
- map:映射方法
public class StreamDemo2 {
public static List<Employee> list = new ArrayList<>();
static {
list.add(new Employee(101, "AAA", 23, 5489));
list.add(new Employee(102, "BBB", 21, 4587));
list.add(new Employee(103, "CCC", 22, 2513));
list.add(new Employee(104, "DDD", 26, 6489));
list.add(new Employee(105, "EEE", 25, 7000));
list.add(new Employee(106, "FFF", 24, 4050));
}
@Test
public void test02(){
List<String> stringList= Arrays.asList("aa","bb","cc","dd","ee");
// map映射转大写
stringList.stream().map(s->s.toUpperCase()).forEach(System.out::println);
// 长度超过5的名字输出
list.add(new Employee(106, "FFFFFF", 24, 4050));
list.stream().map(s->s.getName()).filter(s->s.length() > 5).forEach(System.out::println);
}
}
排序:
- sorted:给自然元素进行排序
- sorted:制定比较规则,再进行排序
public class StreamDemo2 {
public static List<Employee> list = new ArrayList<>();
static {
list.add(new Employee(101, "AAA", 23, 5489));
list.add(new Employee(102, "BBB", 21, 4587));
list.add(new Employee(103, "CCC", 22, 2513));
list.add(new Employee(104, "DDD", 26, 6489));
list.add(new Employee(105, "EEE", 24, 7000));
list.add(new Employee(106, "FFF", 24, 4050));
}
@Test
public void test03() {
List<Integer> integers = Arrays.asList(99, 12, 24, 11, 45, 56, 78, 42, 66);
// 对自然元素进行排序
integers.stream().sorted().forEach(System.out::println);
System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++");
// 对员工年龄进行排序
list.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).forEach(System.out::println);
System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++");
// 先对员工年龄进行排序,如果年龄一样,再按照薪资进行排序
list.stream().sorted((e1, e2) -> {
// 先比较年龄
int compare = Integer.compare(e1.getAge(), e2.getAge());
if (compare != 0) {
return compare;
} else {
return Integer.compare(e1.getSalary(), e2.getSalary());
}
}).forEach(System.out::println);
}
}
4.3.2. 终结方法
终结方法:
- forEach:逐一处理
- count:统计个数
public class StreamDemo3 {
public static List<Employee> list = new ArrayList<>();
static {
list.add(new Employee(101, "AAA", 23, 5489));
list.add(new Employee(102, "BBB", 21, 4587));
list.add(new Employee(103, "CCC", 22, 2513));
list.add(new Employee(104, "DDD", 26, 6489));
list.add(new Employee(105, "EEE", 24, 7000));
list.add(new Employee(106, "FFF", 24, 4050));
}
@Test
public void test01() {
// 查询所有员工总数
long count = list.stream().count();
System.out.println("总数是: " + count);
// 查询薪资在4000元的员工个数
long count_salary = list.stream().filter(e -> e.getSalary() >= 4000).count();
System.out.println(count_salary);
// 找出集合中薪资最高的薪水
Integer max_salary = list.stream().map(i -> i.getSalary()).max((o1, o2) -> o1 - o2).get();
System.out.println(max_salary);
}
}
Stream流中的其他操作:
@Test
public void test03(){
List<Employee> empList = EmployeeData.employeeList;
// collect 将过滤之后的元素放在新的集合中
List<Employee> emps = empList.stream().filter(e -> e.getSalary() > 5000).collect(Collectors.toList());
// 循环遍历集合中的元素
emps.forEach(System.out::println);
}
5 Optional类
Optional类主要是避免在开发中对象出现空指针异常
5.1. Optional对象创建
Optional.of(T t):根据指定对象构建一个Optional类型的对象,of里面的对象一定不能为空,否则出现空指针异常
Optional.ofnullable(T t):根据指定对象构建一个Optional类型的对象,ofNullable方法里面可以传递空对象,也可以传递非空对象
public class OptionalDemo1 {
@Test
public void test01() {
Girl girl = new Girl();
// 必须传入一个非空对象,否则报错
Optional<Girl> optional1 = Optional.of(girl);
System.out.println(optional1);
// Optional<Girl> optional2 = Optional.of(null); 报错
// System.out.println(optional2);
}
@Test
public void test02(){
Girl girl = new Girl();
Optional<Girl> optional1 = Optional.ofNullable(girl);
System.out.println(optional1);
Optional<Object> optional2 = Optional.ofNullable(null);
System.out.println(optional2);
}
}
class Girl {
}
5.2. Optional类的基本使用
public class OptionalDemo2 {
// 使用 Optional 类来优化方法
public String getGirlName(Boy boy) {
Optional<Boy> boyOptional = Optional.ofNullable(boy);
// 通过orElse方法来避免空指针异常出现的风险,如果boy对象为null,则使用orElse里面的默认对象
Boy boy1 = boyOptional.orElse(new Boy(null));
Girl girl = boy1.getGirl();
// 如果girl对象为null时,也用orElse方法来判空,并设置默认值
Optional<Girl> girlOptional = Optional.ofNullable(girl);
Girl girl1 = girlOptional.orElse(new Girl("AAA"));
return girl1.getName();
}
@Test
public void test01() {
Boy boy = null;
String girlName = getGirlName(boy);
System.out.println(girlName);
Boy boy1 = new Boy(new Girl("BBB"));
System.out.println(getGirlName(boy1));
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Boy {
Girl girl;
}