javaSE基础-jdk8新特性
jdk8新特性
Lambda表达式
语法示例
示例一
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("new Runnable。。。");
}
};
r1.run();
System.out.println("******************");
Runnable r2 = () -> System.out.println("Lambda new Runnable...");
r2.run();
}
示例二
@Test
public void test2(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
int compare1 = com1.compare(11, 23);
System.out.println(compare1);
System.out.println("*******************");
//Lambda表达式写法
//Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1,o2);
Comparator<Integer> com2 = Comparator.comparingInt(o -> o);
int compare2 = com2.compare(43, 9);
System.out.println(compare2);
System.out.println("*******************");
//方法引用
Comparator<Integer> com3 = Integer::compareTo;
int compare3 = com3.compare(32, 99);
System.out.println(compare3);
}
基本语法
1、举例:(o1,o2) -> Integer.compare(o1,o2);
2、格式:
-> : lambda操作符 或 箭头操作符
->左边:Lambda形参列表(其实就是接口中的抽象方法的形参列表)
->右边:lambda体(其实就是重写的抽象方法的方法体)
3、Lambda表达式的使用:(分为6种情况)
//语法格式一:无参,无返回值
Runnable r1 = () -> { System.out.println("Runnable...");};
r1.run();
//语法格式二:有一个参数,无返回值
Consumer<String> c2 = (String s) ->{System.out.println(s);};
c2.accept("一个是听得人当真了,一个是说得的人当真了");
//语法格式三:数据类型可以省略(编译器可以推断类型,即类型推断),无返回值
Consumer<String> c2 = (s) ->{System.out.println(s);};
c2.accept("一个是听得人当真了,一个是说得的人当真了");
//语法格式四:只需要一个参数时,参数的小括号可以省略
Consumer<String> c2 = s ->{System.out.println(s);};
c2.accept("一个是听得人当真了,一个是说得的人当真了");
//语法格式五:lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(11,5));
//语法格式六:lambda体只有一条语句,return 和 大括号 若有都可以省略
Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(com2.compare(11,5));
->左边:Lambda形参列表的参数类型可以省略(类型推断),如果Lambda形参列表只有一个参数,其实一对{}花括号可以省略
->右边:lambda体应该使用一对{}大括号包裹,如果Lambda体只有一条执行语句(可能是return语句),可以省略这一对{} 和 return关键字
之前使用的匿名实现类表示的现在可以用lambda表达式来写
函数式(Functional)接口
概念:如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。
在一个接口上使用@FunctionalInterface注解,可以检查它是否是一个函数式接口
Lambda表达式的本质:就是函数式接口的实例
什么情况下可以使用Lambda表达式?
当需要对一个函数式接口实例化的时候,可以使用Lambda表达式
Java8内置四个基本的函数式接口
Consumer接口
@Test
public void test1(){
happyTime(300, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("今天挣了:" + aDouble + "$");
}
});
System.out.println("******************");
happyTime(400,money -> System.out.println("加班挣了 " + money + "$"));
}
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
//结果
今天挣了:300.0$
******************
加班挣了 400.0$
Supplier接口
@Test
public void test9(){
Supplier<String> sup = () -> "Hi! Kim.";
System.out.println(sup.get());
}
//结果
Hi! Kim.
Function接口
@Test
public void test8(){
Function<String, String> func = str -> str + "Tim";
System.out.println(func.apply("Hello! "));
}
//结果
Hello! Tim
Predicate接口
@Test
public void test2(){
List<String> li = Arrays.asList("Tom", "kim", "Jom", "pig");
List<String> filterList = filterString(li, new Predicate<String>() {
@Override
public boolean test(String str) {
return str.contains("m");
}
});
System.out.println(filterList);
System.out.println("****************");
List<String> filterList2 = filterString(li, str -> str.contains("m"));
System.out.println(filterList2);
}
//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> strList = new ArrayList<>();
for (String s : list) {
if(pre.test(s)){
strList.add(s);
}
}
return strList;
}
//结果
[Tom, kim, Jom]
****************
[Tom, kim, Jom]
方法引用与构造器引用
方法引用
1、场景:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
2、方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。方法引用,也就是函数式接口的实例
3、使用格式:类(对象) :: 方法名
4、具体分为如下的三种情况:
情况1 对象 :: 非静态方法
情况2 类 :: 静态方法
情况3 类 :: 非静态方法
5、方法引用使用的要求:
【情况1和情况2】: 要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法形参列表和返回值类型相同
【情况3】:当函数式接口的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时:ClassName :: methodName
实体类
//Employee.java
public class Employee {
private int id;
private String name;
private int age;
private double salary;
...
}
示例
//MethodRefTest.java
public class MethodRefTest {
/**
* 情况一:对象::实例方法
* Consumer 中的 void accept(T t)
* PrintStream 中的 void println(T t)
*/
@Test
public void test1(){
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("上海");
System.out.println("*********");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("shanghai");
}
/**
* Supplier中的 T get()
* Employee中的 String getName()
*/
@Test
public void test2(){
Employee emp = new Employee(1001, "Tim", 22, 6600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("*********");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
/**
* 情况二: 类:: 静态方法
* Comparator中的 int compare(T t1, T t2)
* Integer中的 int compare(T t1, T t2)
*/
@Test
public void test3(){
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(com1.compare(11,27));
System.out.println("******");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12,4));
}
/**
* Function中的 R apply(T t)
* Math 中的 Long round(Double d)
*/
@Test
public void test4(){
Function<Double, Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return Math.round(aDouble);
}
};
System.out.println(func.apply(12.22));
System.out.println("*******");
Function<Double, Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(11.23));
System.out.println("*******");
Function<Double,Long> func2 = Math::round;
System.out.println(func2.apply(11.23));
}
/**
* 情况三: 类::实例方法
* Comparator中的 int compare(T t1, T t2)
* String中 int t1.compareTo(T2)
*/
@Test
public void test5(){
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc", "abd"));
System.out.println("*******");
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abc","abe"));
}
/**
* BiPredicate中的boolean test(T t1, T t2);
* String中的boolean t1.equals(t2)
*/
@Test
public void test6(){
BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2);
System.out.println(pre1.test("abs", "abs"));
System.out.println("********");
BiPredicate<String,String> pre2 = String::equals;
System.out.println(pre2.test("abs", "abc"));
}
/**
* Function中的 R apply(T t)
* Employee中的String getName();
*/
@Test
public void test7(){
Employee emp = new Employee(1001, "Tim", 22, 6600);
Function<Employee, String> func1 = e -> e.getName();
System.out.println(func1.apply(emp));
System.out.println("*********");
Function<Employee, String> func2 = Employee::getName;
System.out.println(func2.apply(emp));
}
}
构造器引用
与方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致,抽象方法的返回值类型即为构造器所属的类的类型
数组引用:把数组看作是一个特殊的类,则写法与构造器引用一样
格式:类名 :: new / 数组类型[] :: new
示例:构造器引用
/**
* 构造器引用 - 无参
* Supplier中的 T get()
*/
@Test
public void test1(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("****************");
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());//Employee{id=0, name='null', age=0, salary=0.0}
System.out.println("****************");
//调用 public Employee()
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());//Employee{id=0, name='null', age=0, salary=0.0}
}
/**
* 构造器引用 - 一个参数
* Function中的 R apply(T t)
*/
@Test
public void test2(){
Function<Integer, Employee> func1 = id -> new Employee(id);
System.out.println(func1.apply(1001));//Employee{id=1001, name='null', age=0, salary=0.0}
System.out.println("****************");
//调用 public Employee(int id)
Function<Integer,Employee> func2 = Employee::new;
System.out.println(func2.apply(1002));//Employee{id=1002, name='null', age=0, salary=0.0}
}
/**
* 构造器引用 - 两个参数
* BiFunction中的 R apply(T t, U u)
*/
@Test
public void test3(){
BiFunction<Integer, String, Employee> biFunc1 = (id,name) -> new Employee(id,name);
System.out.println(biFunc1.apply(1001,"Tim"));//Employee{id=1001, name='Tim', age=0, salary=0.0}
System.out.println("****************");
//调用 public Employee(int id, String name)
BiFunction<Integer,String,Employee> biFunc2 = Employee::new;
System.out.println(biFunc2.apply(1002,"Mark"));//Employee{id=1002, name='Mark', age=0, salary=0.0}
}
示例:数组引用
/**
* 数组引用
* Function 中 R apply(T t)
*/
@Test
public void test4(){
Function<Integer, String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(3);
System.out.println(Arrays.toString(arr1));//[null, null, null]
System.out.println("****************");
Function<Integer, String[]> func2 = String[]::new;
String[] arr2 = func2.apply(3);
System.out.println(Arrays.toString(arr2));//[null, null, null]
}
强大的Stream API
1、Stream关注的是对数据的运算,与CPU打交道,集合关注的是数据存储,与内存打交道
2、特点
-
Stream自己不会存储元素
-
Stream不会改变源对象,相反,它们会返回一个持有结果的新的Stream
-
Stream操作是延迟执行的,这意味着它们等到需要结果的时候才执行
3、Stream执行流程
-
Stream的实例化
-
一系列的中间操作(过滤、映射)
-
终止操作
4、说明
一个中间操作链,对数据源的数据进行处理
一旦执行终止操作,就执行中间操作链,并产生结果。之后不会再使用
原始数据
//EmployeeData.java
public class EmployeeData {
public static List<Employee> getEmployees(){
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee(1001, "张三", 33, 8763.00));
list.add(new Employee(1002, "李四", 22, 1223.00));
list.add(new Employee(1003, "王五", 53, 3366.00));
list.add(new Employee(1004, "赵六", 19, 6763.00));
list.add(new Employee(1005, "李伟", 56, 10063.00));
list.add(new Employee(1006, "钱一一", 33, 9993.00));
list.add(new Employee(1007, "吴海", 21, 7903.00));
list.add(new Employee(1008, "欧阳怪怪", 35, 8953.00));
return list;
}
}
Stream的实例化 - 四种方式
//方式一:利用集合
@Test
public void test1(){
//default Stream<E> stream():返回一个顺序流
List<Employee> employees = EmployeeData.getEmployees();
Stream<Employee> stream = employees.stream();
//default Stream<E> parallelStream():返回一个并行流
Stream<Employee> employeeStream = employees.parallelStream();
}
//方式二:数组
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Mark");
Employee[] arr2 = new Employee[]{e1, e2};
Stream<Employee> stream1 = Arrays.stream(arr2);
}
//方式三:通过Stream的of()
@Test
public void test3(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4);
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Mark");
Stream<Employee> employeeStream = Stream.of(e1, e2);
}
//方式四:创建无限流
@Test
public void test4(){
//迭代
//public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//遍历输出 5个偶数
Stream.iterate(0,t -> t+2).limit(5).forEach(System.out::println);
//生成
//public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(6).forEach(System.out::println);
}
中间操作
筛选与切片
@Test
public void test1(){
List<Employee> employeeList = EmployeeData.getEmployees();
Stream<Employee> stream = employeeList.stream();
//filter(Predicate p):接收Lambda,从流中排除某些元素
stream.filter(e -> e.getSalary()>6000).forEach(System.out::println);
System.out.println();
//limit(n):截断流,使其元素不超过给定数量
employeeList.stream().limit(3).forEach(System.out::println);
System.out.println();
//skip(n):跳过元素,返回一个扔掉了前几个n个元素的流,若流中元素不足n个,则返回一个空流
employeeList.stream().skip(3).forEach(System.out::println);
System.out.println();
employeeList.add(new Employee(1010,"李宁",45,9000.89));
employeeList.add(new Employee(1010,"李宁",45,9000.89));
employeeList.add(new Employee(1010,"李宁",45,9000.89));
employeeList.add(new Employee(1010,"李宁",45,9000.89));
employeeList.forEach(System.out::println);
System.out.println();
//distinct():筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素
employeeList.stream().distinct().forEach(System.out::println);
}
映射
@Test
public void test2(){
//map(Function f): 接收一个函数作为参数,将元素转换成其他形式或提取信息,
//该函数会被应用到每一个元素上,并将其映射成一个流
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(String::toUpperCase).forEach(System.out::println);//AA BB CC DD
System.out.println();
//练习1:获取员工姓名长度大于3的员工的姓名
List<Employee> employees = EmployeeData.getEmployees();
Stream<String> namesStream = employees.stream().map(Employee::getName);
namesStream.filter(name -> name.length()>3).forEach(System.out::println);// 欧阳怪怪
System.out.println();
//练习2:
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream);
streamStream.forEach(s ->{
s.forEach(System.out::println);// a a b b c c d d
});
System.out.println();
//flatMap(Function f): 接收一个函数作为参数,将流中的每个值都换成另外一个流,
//然后把所有的流连成一个流
Stream<Character> streamStream2 = list.stream().flatMap(StreamAPITest2::fromStringToStream);
streamStream2.forEach(System.out::println);// a a b b c c d d
}
//将字符串中的多个字符构成的集合转换为对应的Stream的实例
public static Stream<Character> fromStringToStream(String str){
ArrayList<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
排序
@Test
public void test4(){
//sorted():自然排序
List<Integer> list = Arrays.asList(12, 45, 233, -1, 0, 19, 3, 7, 14);
list.stream().sorted().forEach(System.out::println);
//报错:Employee类没有实现Comparable接口
//List<Employee> employees = EmployeeData.getEmployees();
//employees.stream().sorted().forEach(System.out::println);
//sorted(Comparator com):定制排序
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((e1,e2) ->{
//排序规则:按年龄从小到大,salary从大到小
int returnVal = Integer.compare(e1.getAge(), e2.getAge());
if(returnVal != 0){
return returnVal;
}else {
return -Double.compare(e1.getSalary(),e2.getSalary());
}
}).forEach(System.out::println);
}
终止操作
匹配与查找
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees();
//allMatch(Predicate p):检查是否匹配所有元素
//练习1:是否所有的员工的年龄都大于18
boolean b = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(b);
//anyMatch(Predicate p):检查是否匹配一个元素
//练习2:是否存在员工的工资大于8000
boolean b1 = employees.stream().anyMatch(e -> e.getSalary() > 8000);
System.out.println(b1);
//noneMatch(Predicate p):检查是否没有匹配的元素(没有:返回true)
//练习3:是否存在在员工姓"钱"
boolean b3 = employees.stream().noneMatch(e -> e.getName().startsWith("钱"));
System.out.println(b3);//false
//findFirst:返回第一个元素
Optional<Employee> employee = employees.stream().findFirst();
System.out.println(employee);
//findAny:返回当前流中的任意元素
Optional<Employee> any = employees.stream().findAny();
System.out.println(any);
//count:返回流中元素的总个数
long count = employees.stream().filter(e -> e.getSalary() > 7000).count();
System.out.println(count);
//max(Comparator c):返回流中最大值
//练习4:返回最高的工资
Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
Optional<Double> maxSalary = salaryStream.max(Double::compare);
System.out.println(maxSalary);
//min(Comparator c):返回流中最小值
//练习5:返回最低的工资
Optional<Employee> minEmployee = employees.stream()
.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(minEmployee);
//forEach(Consumer c):内部迭代
employees.stream().forEach(System.out::println);
//集合的遍历
employees.forEach(System.out::println);
}
归约
@Test
public void test2(){
//reduce(T identity, BinaryOperator): 可以将流中元素反复结合起来,得到一个值,返回T
//练习1:计算1-10的自然数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
Optional<Integer> sum2 = list.stream().reduce((t1, t2) -> t1 + t2);
System.out.println(sum2);
//reduce(BinaryOperator):可以将流中的元素反复结合结合起来,得到一个值。返回Optional<T>
//练习2:计算公司所有员工工资的总和
List<Employee> employees = EmployeeData.getEmployees();
//Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
Optional<Double> salarySum = salaryStream.reduce(Double::sum);
System.out.println(salarySum);
}
收集
@Test
public void test4(){
//collect(Collector c):将流转换为其他形式。
//接收一个Collector接口的实现,用于给Set、List、Map等
//练习:查找工资大于6000的员工,结果返回一个List或Set
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);
Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}
Collector接口的实现类:Collectors API
Optional类
Optional <T> 类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。使用Optional能更好表达null,避免空指针异常。
Optional类常用方法
示例一
@Test
public void test1(){
Optional<Object> op1 = Optional.empty();
//isPresent():是否包含对象
if(!op1.isPresent()){
System.out.println("数据为空");
}
System.out.println(op1);//Optional.empty
System.out.println(op1.isPresent());//false
}
/**
* Optional.of()
* 对象.get()
*/
@Test
public void test2(){
String str1= "Hi";
Optional<String> op1 = Optional.of(str1);
String str2 = op1.get();
System.out.println(str2);//Hi
}
/**
* Optional.ofNullable(T t): 封装数据t赋给Optional内部的value,不要求t非空
* 对象.orElse(T t1):对象Optional内部数据value非空,则返回value;如果空,则返回t1;
*/
@Test
public void test3(){
String str ="Hello";
str = null;
Optional<String> op1 = Optional.ofNullable(str);
String str1 = op1.orElse("hi");
System.out.println(str1); // 非空:Hello / 空:hi
}
实体类
//Girl.java
public class Girl {
private String name;
public Girl() {}
...
}
//Boy.java
public class Boy {
private Girl girl;
public Boy() {}
public Boy(Girl girl) {
this.girl = girl;
}
...
}
示例二:使用Optional类封装Boy类和Girl类的对象
//优化之后:避免空指针异常
public String getGirlName2(Boy boy){
if(boy != null){
Girl girl = boy.getGirl();
if(girl != null){
String girlName = girl.getName();
return girlName;
}
}
return null;
}
@Test
public void test4(){
Boy boy = new Boy();
boy = null;
String girlName = getGirlName2(boy);
System.out.println(girlName);
}
//使用Optional类优化之后:避免空指针异常
public String getGirlName3(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();
}
@Test
public void test5(){
Boy boy = new Boy();// 艾丽
boy = null;// 娜扎
boy = new Boy(new Girl("茉莉"));// 茉莉
String girlName3 = getGirlName3(boy);
System.out.println(girlName3);
}