01、Java8其他新特性 上述讲过的Java8新特性:
Java常用类中的日期API LocalDateTime
等;
注解 类型注解等;
集合 底层的变化,比如底层数组的延迟创建,红黑树的出现;
Java 8 (又称为jdk 1.8) 是Java 语言开发的一个主要版本。 Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
02、Java8新特性的好处 速度更快 代码更少(增加了新的语法: Lambda 表达式 ) 强大的Stream API 便于并行 最大化减少空指针异常:Optional Nashorn引擎,允许在JVM上运行JS应用 03、并行流与串行流 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel() 与sequential() 在并行流与顺序流之间进行切换。
04、Lambda表达式 Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
4.1、Lambda表达式使用举例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import org.junit.Test;import java.util.Comparator;public class LambdaTest { @Test public void test () { Runnable r1 = new Runnable () { @Override public void run () { System.out.println("长安欢迎您" ); } }; r1.run(); System.out.println("+++++++++++++++++++++++++|" ); Runnable r2 = () -> System.out.println("长安欢迎您" ); r2.run(); } @Test public void test2 () { Comparator<Integer> c1 = new Comparator <Integer>() { @Override public int compare (Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; int compare1 = c1.compare(8 ,16 ); System.out.println(compare1); System.out.println("+++++++++++++++++++++++" ); Comparator<Integer> c2 = (o1,o2) -> Integer.compare(o1,o2); int compare2 = c2.compare(28 ,35 ); System.out.println(compare2); System.out.println("+++++++++++++++++++++++++++" ); Comparator<Integer> c3 = Integer :: compare; int compare3 = c3.compare(28 ,35 ); System.out.println(compare3); } }
4.2、Lambda表达式语法的使用1 总结:
->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略 ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键 3.Lambda表达式的使用:(分为6种情况介绍)
语法格式一:无参,无返回值
语法格式二:Lambda 需要一个参数,但是没有返回值。
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
语法格式四:Lambda若只需要一个参数时,参数的小括号可以省略
语法格式五:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
语法格式六:当Lambda体只有一条语句时,return与大括号若有,都可以省略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 import org.junit.Test;import java.util.ArrayList;import java.util.function.Consumer;public class LambdaTest1 { @Test public void test () { Runnable r1 = new Runnable () { @Override public void run () { System.out.println("长安欢迎您" ); } }; r1.run(); System.out.println("+++++++++++++++++++++++++|" ); Runnable r2 = () -> System.out.println("长安欢迎您" ); r2.run(); } @Test public void test2 () { Consumer<String> con = new Consumer <String>() { @Override public void accept (String s) { System.out.println(s); } }; con.accept("善与恶的区别是什么?" ); System.out.println("+++++++++++++++++++" ); Consumer<String> c1 = (String s) -> { System.out.println(s); }; c1.accept("先天人性无善恶,后天人性有善恶。" ); } @Test public void test3 () { Consumer<String> c1 = (String s) -> { System.out.println(s); }; c1.accept("先天人性无善恶,后天人性有善恶。" ); System.out.println("---------------------" ); Consumer<String> c2 = (s) -> { System.out.println(s); }; c2.accept("如果没有邪恶的话我们怎么会知道人世间的那些善良呢?" ); } @Test public void test4 () { ArrayList<String> list = new ArrayList <>(); int [] arr = {1 ,2 ,3 }; } }
4.3、Lambda表达式语法的使用2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 import org.junit.Test;import java.util.Comparator;import java.util.function.Consumer;public class LambdaTest1 { @Test public void test5 () { Consumer<String> c1 = (s) -> { System.out.println(s); }; c1.accept("先天人性无善恶,后天人性有善恶。" ); System.out.println("---------------------" ); Consumer<String> c2 = s -> { System.out.println(s); }; c2.accept("如果没有邪恶的话我们怎么会知道人世间的那些善良呢?" ); } @Test public void test6 () { Comparator<Integer> c1 = new Comparator <Integer>() { @Override public int compare (Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(c1.compare(15 ,23 )); System.out.println("\\\\\\\\\\\\\\\\\\\\\\\\\\" ); Comparator<Integer> com2 = (o1,o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com2.compare(16 ,8 )); } @Test public void test7 () { Comparator<Integer> c1 = (o1,o2) -> { return o1.compareTo(o2); }; System.out.println(c1.compare(16 ,8 )); System.out.println("\\\\\\\\\\\\\\\\\\\\\\\\\\" ); Comparator<Integer> c2 = (o1,o2) -> o1.compareTo(o2); System.out.println(c2.compare(17 ,24 )); } @Test public void test8 () { Consumer<String> c1 = s -> { System.out.println(s); }; c1.accept("先天人性无善恶,后天人性有善恶。" ); System.out.println("---------------------" ); Consumer<String> c2 = s -> System.out.println(s); c2.accept("如果没有邪恶的话我们怎么会知道人世间的那些善良呢?" ); } }
05、函数式(Functional)接口 5.1、函数式接口的介绍 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public interface MyInterFace { void method () ; }
在java.util.function
包下定义了Java 8 的丰富的函数式接口 Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程) 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示 。 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。 5.2、Java内置的函数式接口介绍及使用举例 java内置的4大核心函数式接口:
Consumer
消费型接口T void 对类型为T的对象应用操作,包含方法:void accept(T t)
Supplier
供给型接口无 T 返回类型为T的对象,包含方法:T get()
Function<T, R>
函数型接口T R 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
Predicate
断定型接口T boolean 确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法:boolean test(T t)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import org.junit.Test;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.function.Consumer;import java.util.function.Predicate;public class LambdaTest2 { public void happyTime (double money, Consumer<Double> con) { con.accept(money); } @Test public void test () { happyTime(30 , new Consumer <Double>() { @Override public void accept (Double aDouble) { System.out.println("熬夜太累了,点个外卖,价格为:" + aDouble); } }); System.out.println("+++++++++++++++++++++++++" ); happyTime(20 ,money -> System.out.println("熬夜太累了,吃口麻辣烫,价格为:" + money)); } public List<String> filterString (List<String> list, Predicate<String> pre) { ArrayList<String> filterList = new ArrayList <>(); for (String s : list){ if (pre.test(s)){ filterList.add(s); } } return filterList; } @Test public void test2 () { List<String> list = Arrays.asList("长安" ,"上京" ,"江南" ,"渝州" ,"凉州" ,"兖州" ); List<String> filterStrs = filterString(list, new Predicate <String>() { @Override public boolean test (String s) { return s.contains("州" ); } }); System.out.println(filterStrs); List<String> filterStrs1 = filterString(list,s -> s.contains("州" )); System.out.println(filterStrs1); } }
Consumer
消费型接口T void 对类型为T的对象应用操作,包含方法:void accept(T t)
Supplier
供给型接口无 T 返回类型为T的对象,包含方法:T get()
Function<T, R>
函数型接口T R 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
Predicate
断定型接口T boolean 确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法:boolean test(T t)
BiFunction<T,U,R>
T, U R 对类型为T,U参数应用操作,返回R类型的结果。包含方法为:Rapply(T t,U u)
; UnaryOperator
(Function子接口)T T 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为:Tapply(T t)
; BinaryOperator
(BiFunction子接口)T,T T 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为:Tapply(T t1,T t2)
; BiConsumer<T,U>
T,U void 对类型为T,U参数应用操作。包含方法为:voidaccept(Tt,Uu)
BiPredicate<T,U>
T,U boolean 包含方法为:booleantest(Tt,Uu)
ToIntFunction
T int 计算int
值的函数 ToLongFunction
T long 计算long
值的函数 ToDoubleFunction
T double 计算double
值的函数 IntFunction
int R 参数为int
类型的函数 LongFunction
long R 参数为long
类型的函数 DoubleFunction
double R 参数为double
类型的函数
06、方法引用与构造器引用 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
格式:使用操作符“::” 将类(或对象) 与方法名分隔开来。
如下三种主要使用情况:
对象::实例方法名 类::静态方法名 类::实例方法名 方法引用的使用
1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例 。
使用格式: 类(或对象) :: 方法名
具体分为如下的三种情况: 情况1 对象 :: 非静态方法
情况2 类 :: 静态方法
情况3 类 :: 非静态方法
方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同! (针对于情况1和情况2)
6.1、方法引用的使用情况1 1、Employee类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 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; } @Override public String toString () { return "Employee{" + "id=" + id + ", 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 ; 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 ; } @Override public 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; } }
2、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import org.junit.Test;import java.io.PrintStream;import java.util.Comparator;import java.util.function.BiPredicate;import java.util.function.Consumer;import java.util.function.Function;import java.util.function.Supplier;public class MethodRefTest { @Test public void test () { Consumer<String> c1 = str -> System.out.println(str); c1.accept("兖州" ); System.out.println("+++++++++++++" ); PrintStream ps = System.out; Consumer<String> c2 = ps::println; c2.accept("xian" ); } @Test public void test2 () { Employee emp = new Employee (004 ,"Nice" ,19 ,4200 ); Supplier<String> sk1 = () -> emp.getName(); System.out.println(sk1.get()); System.out.println("*******************" ); Supplier<String> sk2 = emp::getName; System.out.println(sk2.get()); } }
6.2、方法引用的使用情况2 1、Employee类——同上
2、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import org.junit.Test;import java.util.Comparator;import java.util.function.Function;public class MethodRefTest { @Test public void test3 () { Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(21 ,20 )); System.out.println("+++++++++++++++" ); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(15 ,7 )); } @Test public void test4 () { Function<Double,Long> func = new Function <Double, Long>() { @Override public Long apply (Double d) { return Math.round(d); } }; System.out.println("++++++++++++++++++" ); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(14.1 )); System.out.println("++++++++++++++++++" ); Function<Double,Long> func2 = Math::round; System.out.println(func2.apply(17.4 )); } }
6.2、方法引用的使用情况3 1、Employee类——同上
2、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import org.junit.Test;import java.util.Comparator;import java.util.function.BiPredicate;import java.util.function.Function;public class MethodRefTest { @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("abd" ,"abm" )); } @Test public void test6 () { BiPredicate<String,String> pre1 = (s1, s2) -> s1.equals(s2); System.out.println(pre1.test("MON" ,"MON" )); System.out.println("++++++++++++++++++++" ); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("MON" ,"MON" )); } @Test public void test7 () { Employee employee = new Employee (007 , "Ton" , 21 , 8000 ); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("++++++++++++++++++++++++" ); Function<Employee,String> f2 = Employee::getName; System.out.println(f2.apply(employee)); } }
6.4、构造器引用与数组引用的使用 格式:ClassName::new
与函数式接口相结合,自动与函数式接口中方法兼容。
可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
1、Employee类——同上
2、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 import org.junit.Test;import java.util.Arrays;import java.util.function.BiFunction;import java.util.function.Function;import java.util.function.Supplier;public class MethodRefTest { @Test public void test () { Supplier<Employee> sup = new Supplier <Employee>() { @Override public Employee get () { return new Employee (); } }; System.out.println("+++++++++++++++++++" ); Supplier<Employee> sk1 = () -> new Employee (); System.out.println(sk1.get()); System.out.println("+++++++++++++++++++" ); Supplier<Employee> sk2 = Employee::new ; System.out.println(sk2.get()); } @Test public void test2 () { Function<Integer, Employee> f1 = id -> new Employee (id); Employee employee = f1.apply(7793 ); System.out.println(employee); System.out.println("+++++++++++++++++++" ); Function<Integer, Employee> f2 = Employee::new ; Employee employee1 = f2.apply(4545 ); System.out.println(employee1); } @Test public void test3 () { BiFunction<Integer, String, Employee> f1 = (id, name) -> new Employee (id, name); System.out.println(f1.apply(2513 , "Fruk" )); System.out.println("*******************" ); BiFunction<Integer, String, Employee> f2 = Employee::new ; System.out.println(f2.apply(9526 , "Bon" )); } @Test public void test4 () { Function<Integer, String[]> f1 = length -> new String [length]; String[] arr1 = f1.apply(7 ); System.out.println(Arrays.toString(arr1)); System.out.println("+++++++++++++++++++" ); Function<Integer, String[]> f2 = String[]::new ; String[] arr2 = f2.apply(9 ); System.out.println(Arrays.toString(arr2)); } }
07、强大的Stream API 7.1、Stream API的概述 Java8中有两大最为重要的改变。第一个是Lambda 表达式;另外一个则是Stream API。 Stream API ( java.util.stream
)把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。 Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。 为什么要使用Stream API 实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理 。Stream 和Collection 集合的区别:Collection 是一种静态的内存数据结构,而Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU 实现计算 。1.Stream关注的是对数据的运算,与CPU打交道;集合关注的是数据的存储,与内存打交道
3.Stream 执行流程
① Stream的实例化
② 一系列的中间操作(过滤、映射、...)
③ 终止操作
4.说明: 4.1 一个中间操作链,对数据源的数据进行处理
4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
7.2、Stream的实例化 创建 Stream方式一:通过集合
1 2 List<Employee> employees = EmployeeData.getEmployees(); Stream<Employee> stream = employees.stream();
创建 Stream方式二:通过数组
1 2 3 int [] arr = new int []{1 ,2 ,3 ,4 ,5 ,6 };IntStream stream = Arrays.stream(arr);
创建 Stream方式三:通过Stream的of()
1 Stream<Integer> stream = Stream.of(1 , 2 , 3 , 4 , 5 , 6 );
创建 Stream方式四:创建无限流
1 2 3 4 Stream.iterate(0 , t -> t + 2 ).limit(10 ).forEach(System.out::println);
1、EmployeeData类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.util.ArrayList;import java.util.List;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; } }
2、Employee类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 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; } @Override public String toString () { return "Employee{" + "id=" + id + ", 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 ; 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 ; } @Override public 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; } }
3、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import github2.Employee;import github2.EmployeeData;import org.junit.Test;import java.util.Arrays;import java.util.List;import java.util.stream.IntStream;import java.util.stream.Stream;public class StreamAPITest { @Test public void test () { List<Employee> employees = EmployeeData.getEmployees(); Stream<Employee> stream = employees.stream(); Stream<Employee> parallelStream = employees.parallelStream(); } @Test public void test2 () { int [] arr = new int []{1 ,2 ,3 ,4 ,5 ,6 }; IntStream stream = Arrays.stream(arr); Employee e1 = new Employee (1001 ,"Hom" ); Employee e2 = new Employee (1002 ,"Nut" ); Employee[] arr1 = new Employee []{e1,e2}; Stream<Employee> stream1 = Arrays.stream(arr1); } @Test public void test3 () { Stream<Integer> stream = Stream.of(1 , 2 , 3 , 4 , 5 , 6 ); } @Test public void test4 () { Stream.iterate(0 , t -> t + 2 ).limit(10 ).forEach(System.out::println); Stream.generate(Math::random).limit(10 ).forEach(System.out::println); } }
7.3、Stream的中间操作:筛选与切片 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
filter(Predicate p)
接收Lambda ,从流中排除某些元素 distinct()
筛选,通过流所生成元素的hashCode() 和equals() 去除重复元素 limit(long maxSize)
截断流,使其元素不超过给定数量 skip(long n)
跳过元素,返回一个扔掉了前n 个元素的流。若流中元素不足n 个,则返回一个空流。与limit(n)
互补
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import github2.Employee;import github2.EmployeeData;import org.junit.Test;import java.util.List;import java.util.stream.Stream;public class StreamAPITest2 { @Test public void test () { List<Employee> list = EmployeeData.getEmployees(); Stream<Employee> stream = list.stream(); stream.filter(e -> e.getSalary() > 7000 ).forEach(System.out::println); System.out.println("+++++++++++++++++++++++" ); list.stream().limit(3 ).forEach(System.out::println); System.out.println("+++++++++++++++++++++++" ); list.stream().skip(3 ).forEach(System.out::println); System.out.println("+++++++++++++++++++++++" ); list.add(new Employee (1013 ,"李飞" ,42 ,8500 )); list.add(new Employee (1013 ,"李飞" ,41 ,8200 )); list.add(new Employee (1013 ,"李飞" ,28 ,6000 )); list.add(new Employee (1013 ,"李飞" ,39 ,7800 )); list.add(new Employee (1013 ,"李飞" ,40 ,8000 )); list.stream().distinct().forEach(System.out::println); } }
7.4、Stream的中间操作:映射 map(Function f)
接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 mapToDouble(ToDoubleFunction f)
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream。 mapToInt(ToIntFunction f)
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。 mapToLong(ToLongFunction f)
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream。 flatMap(Function f)
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import github2.Employee;import github2.EmployeeData;import org.junit.Test;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.stream.Stream;public class StreamAPITest2 { @Test public void test2 () { List<String> list = Arrays.asList("aa" , "bb" , "cc" , "dd" ); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); 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(); Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream); streamStream.forEach(s ->{ s.forEach(System.out::println); }); System.out.println("++++++++++++++++++++++" ); Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::fromStringToStream); characterStream.forEach(System.out::println); } 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 test3 () { ArrayList list1 = new ArrayList (); list1.add(25 ); list1.add(33 ); list1.add(14 ); ArrayList list2 = new ArrayList (); list2.add(51 ); list2.add(23 ); list2.add(61 ); list1.addAll(list2); System.out.println(list1); } }
7.5、Stream的中间操作:排序 sorted()
产生一个新流,其中按自然顺序排序 sorted(Comparator com)
产生一个新流,其中按比较器顺序排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import github2.Employee;import github2.EmployeeData;import org.junit.Test;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.stream.Stream;public class StreamAPITest2 { @Test public void test4 () { List<Integer> list = Arrays.asList(25 ,45 ,36 ,12 ,85 ,64 ,72 ,-95 ,4 ); list.stream().sorted().forEach(System.out::println); List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted( (e1,e2) -> { int ageValue = Integer.compare(e1.getAge(),e2.getAge()); if (ageValue != 0 ){ return ageValue; }else { return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); } }
7.6、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 使用内部迭代——它帮你把迭代做了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 import github2.Employee;import github2.EmployeeData;import org.junit.Test;import java.util.List;import java.util.Optional;import java.util.stream.Stream;public class StreamAPITest3 { @Test public void test () { List<Employee> employees = EmployeeData.getEmployees(); boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 23 ); System.out.println(allMatch); boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 9000 ); System.out.println(anyMatch); boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("马" )); System.out.println(noneMatch); Optional<Employee> employee = employees.stream().findFirst(); System.out.println(employee); Optional<Employee> employee1 = employees.parallelStream().findAny(); System.out.println(employee1); } @Test public void test2 () { List<Employee> employees = EmployeeData.getEmployees(); long count = employees.stream().filter(e -> e.getSalary() > 4500 ).count(); System.out.println(count); Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> maxSalary = salaryStream.max(Double::compare); System.out.println(maxSalary); Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(employee); System.out.println(); employees.stream().forEach(System.out::println); employees.forEach(System.out::println); } }
7.7、Stream的终止操作:归约 reduce(T iden, BinaryOperator b)
可以将流中元素反复结合起来,得到一个值。返回T reduce(BinaryOperator b)
可以将流中元素反复结合起来,得到一个值。返回Optional
备注:map 和reduce 的连接通常称为map-reduce 模式,因Google 用它来进行网络搜索而出名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import github2.Employee;import github2.EmployeeData;import org.junit.Test;import java.util.Arrays;import java.util.List;import java.util.Optional;import java.util.stream.Stream;public class StreamAPITest3 { @Test public void test3 () { List<Integer> list = Arrays.asList(72 ,25 ,32 ,34 ,43 ,56 ,81 ,15 ,29 ,71 ); Integer sum = list.stream().reduce(0 , Integer::sum); System.out.println(sum); List<Employee> employees = EmployeeData.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee::getSalary); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney.get()); } }
7.8、Stream的终止操作:收集 collect(Collector c)
将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import github2.Employee;import github2.EmployeeData;import org.junit.Test;import java.util.Arrays;import java.util.List;import java.util.Optional;import java.util.Set;import java.util.stream.Collectors;import java.util.stream.Stream;public class StreamAPITest3 { @Test public void test4 () { List<Employee> employees = EmployeeData.getEmployees(); List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000 ).collect(Collectors.toList()); employeeList.forEach(System.out::println); System.out.println("++++++++++++++++++" ); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000 ).collect(Collectors.toSet()); employeeSet.forEach(System.out::println); } }
Collector
接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map
)。
Collectors
实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
08、Optional类 8.1、Optional类的介绍 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional
类(java.util.Optional
) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null
,表示这个值不存在。原来用null
表示一个值不存在,现在Optional
可以更好的表达这个概念。并且可以避免空指针异常。Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()
方法会返回true
,调用get()
方法会返回该对象。 Optional提供很多有用的方法,这样我们就不用显式进行空值检测。 创建Optional类对象的方法:Optional.of(T t)
: 创建一个Optional 实例,t必须非空;Optional.empty()
: 创建一个空的Optional 实例Optional.ofNullable(T t
):t可以为null 判断Optional容器中是否包含对象:boolean isPresent()
: 判断是否包含对象void ifPresent(Consumer<? super T> consumer)
:如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。 获取Optional容器的对象:T get()
: 如果调用对象包含值,返回该值,否则抛异常T orElse(T other)
:如果有值则将其返回,否则返回指定的other对象。T orElseGet(Supplier<? extends T> other)
:如果有值则将其返回,否则返回由Supplier接口实现提供的对象。T orElseThrow(Supplier<? extends X> exceptionSupplier)
:如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。 1、Boy类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Boy { private Girl girl; public Boy () { } public Boy (Girl girl) { this .girl = girl; } public Girl getGirl () { return girl; } public void setGirl (Girl girl) { this .girl = girl; } @Override public String toString () { return "Boy{" + "girl=" + girl + '}' ; } }
2、Girl类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Girl { private String name; public Girl () { } public Girl (String name) { this .name = name; } public String getName () { return name; } public void setName (String name) { this .name = name; } @Override public String toString () { return "Girl{" + "name='" + name + '\'' + '}' ; } }
3、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import org.junit.Test;import java.util.Optional;public class OptionalTest { @Test public void test () { Girl girl = new Girl (); Optional<Girl> optionalGirl = Optional.of(girl); } @Test public void test2 () { Girl girl = new Girl (); Optional<Girl> optionalGirl = Optional.ofNullable(girl); System.out.println(optionalGirl); Girl girl1 = optionalGirl.orElse(new Girl ("" )); System.out.println(girl1); } }
8.2、Optional类的使用举例 1、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import org.junit.Test;import java.util.Optional;public class OptionalTest { @Test public void test3 () { Boy boy = new Boy (); boy = null ; String girlName = getGirlName(boy); System.out.println(girlName); } private String getGirlName (Boy boy) { return boy.getGirl().getName(); } public String getGirlName1 (Boy boy) { if (boy != null ){ Girl girl = boy.getGirl(); if (girl != null ){ return girl.getName(); } } return null ; } @Test public void test4 () { Boy boy = new Boy (); boy = null ; String girlName = getGirlName1(boy); System.out.println(girlName); } public String getGirlName2 (Boy boy) { Optional<Boy> boyOptional = Optional.ofNullable(boy); Boy boy1 = boyOptional.orElse(new Boy (new Girl ("朱淑贞" ))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl); Girl girl1 = girlOptional.orElse(new Girl ("阿青" )); return girl1.getName(); } @Test public void test5 () { Boy boy = null ; boy = new Boy (); boy = new Boy (new Girl ("李清照" )); String girlName = getGirlName2(boy); System.out.println(girlName); } }