Java 8 新特性 Java8
新增了非常多的特性,我们主要讨论以下几个:
Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
Stream API −新添加的Stream API(java.util.stream)
把真正的函数式编程风格引入到Java中。
Date Time API − 加强对日期与时间的处理。
Optional 类 − Optional
类已经成为 Java 8
类库的一部分,用来解决空指针异常。
Nashorn, JavaScript 引擎 − Java 8
提供了一个新的Nashorn javascript
引擎,它允许我们在JVM
上运行特定的javascript
应用。
1. Lambda 表达式 Lambda
表达式,也可称为闭包,它是推动 Java 8
发布的最重要新特性。
Lambda
允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda
表达式可以使代码变的更加简洁紧凑。
语法 lambda
表达式的语法格式如下:
1 2 3 (parameters) -> expression 或 (parameters) ->{ statements; }
以下是lambda表达式的重要特征:
可选类型声明: 不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号: 一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号: 如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字: 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
Lambda 表达式实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void main(String[] args) { // 普通写法 PersonCallBack p=new PersonCallBack() { @Override public void callback() { System.out.println("写作业!"); } }; new lamdabaTest3().test("小明",p); } public void test(String name,PersonCallBack callBack){ System.out.println(name); callBack.callback(); } } // 函数式接口 @FunctionalInterface interface PersonCallBack{ void callback(); }
很显然,这个并不是一个很简洁的写法,我们采用Java8
的Lambada
表达式来实现,那么如何简化呢?
整个过程:去掉修饰符(public
等)、去掉函数的名字(因为已经赋给变量,变量知道此方法名–往后知道抽象方法唯一,不需要方法名了)、去掉返回值类型(编译器可以推断)、去掉参数类型(编译器可以推断参数类型),最终的结果是下面的形式:
1 2 3 4 // Lambada 写法 PersonCallBack p1= () -> System.out.println("写作业!"); new lamdabaTest3().test("小明",p);
分析: 这样的最终结果就是把”一块代码赋给一个变量”。或者说是”这个被赋给一个变量的函数”就是一个Lambada表达式,由于Lambada可以直接赋给一个”变量”,我们可以把Lambada(这里表示为变量)作为参数传递给函数。但是变量(Lambada表达式)的类型是什么呢?
1 2 3 4 5 // 函数式接口 @FunctionalInterface interface PersonCallBack{ void callback(); }
说明:所有的Lambada的类型都是一个接口,而Lambada表达式本身(“那段代码”)就是一个接口的实现,这是理解Lambada
的一个关键所在,理解上可以这样认为:Lambada
表达式就是产生一个实现接口中唯一的抽象方法的子实现类的对象,因此最终结果:
1 2 // Lambada 写法 PersonCallBack p1= () -> System.out.println("写作业!");
函数式接口: 接口中只有一个需要被实现的抽象函数
说明:为了避免后来的人在接口中增加新的接口函数,导致其有多个接口函数需要被实现,变成非函数式接口,引入了一个新的Annotation
(注解):@FunctionalInterface
。可以把他它放在一个接口前,表示这个接口是一个函数式接口,加上它的接口不会被编译,如果加上此标记就不能再添加其他的抽象方法,否则会报错。它有点像@Override
,都是声明了一种使用意图,避免你把它用错。
总结: lambda
表达式本质是匿名方法
Lambda 表达式的结构 Lambada表达式的语法
1 (param1,param2,param3) -> { }
Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “ ->
”,该操作符被称为 Lambda
操作符或箭头操作符。它将 Lambda
分为两个部分:
左侧:指定了 Lambda
表达式需要的方法参数列表
右侧:指定了 Lambda
体,即 Lambda
表达式要执行的功能
使用说明:
一个 Lambda 表达式可以有零个或多个参数,参数的类型既可以明确声明,也可以根据上下文来推断
圆括号内,方法参数列表之间用逗号相隔
当只有一个参数,且其类型可推导时,圆括号()可省略
Lambda
表达式的主体可包含零条或多条语句,如果Lambda
表达式的主体只有一条语句,花括号{}
可省略,如果有返回值,return
也可以省略,同时body
中的“;”也可以省略。匿名函数的返回类型与该主体表达式一致
如果 Lambda
表达式的主体包含一条以上语句,则表达式必须包含在花括号{}
中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
简单应用 对比匿名内部类做为参数传递和Lambda
表达式作为参数来传递–Runnable,Callable接口(具体看例子)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class LamadaDemo { public static void main(String[] args) { //匿名内部类的形式开启一个线程 new Thread(new Runnable() { @Override public void run() { System.out.println("我爱你!"); } }).start(); //Lambada表达式创建匿名内部类开启一个线程 new Thread(() -> System.out.println("-------------")).start(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class LamadaDemo1 { public static void main(String[] args) { //常见的函数式接口:Runnable、 Comparable--排序(是一个函数式接口吗?) Comparable<Integer> comparable=new Comparable<Integer>() { @Override public int compareTo(Integer o) { return 0; } }; //Lambada表达式的方法 Comparable<Integer> com=(a)->a; int i = com.compareTo(3); System.out.println(i); } }
2. 方法引用 概念: 方法引用其实是Lambda
表达式的另一种写法,当要传递给Lambda
体的操作已经有实现的方法了,可以使用方法引用
语法:使用操作符 “ ::
” 将方法名和对象或类的名字分隔开来
几种常见形式:
类名::静态方法
对象::实例方法
类名::实例方法
二.构造器引用
三 数组引用
注意:
Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保存一致
若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method;不管怎么说,实质还是抽象方法的实现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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 /** 一.方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用"方法引用" * (可以理解为方法引用是Lambda表达式的另外一种表现形式) * * 主要有三种语法格式: * 对象::实例方法 * 类::静态方法名 * 类::实例方法名 * 注意: * 1.Lambda 体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致! * 2.若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method * 二.构造器引用 * * ClassName::new * * 三 数组引用 * * Type::new * * * */ public class TestMethodRef { //数组引用 @Test public void test7(){ Function<Integer,String[]> function = (x) -> new String[x]; String[] strings = function.apply(10); System.out.println(strings); Function<Integer,String[]> function2 = String[]::new; System.out.println(function2.apply(10).length); Supplier<ArrayList<Person>> supplier = () -> new ArrayList<>(); Supplier<ArrayList<Person>> supplier1 = ArrayList::new; ArrayList<Person> people = supplier1.get(); } //构造器引用 @Test public void test5(){ Supplier<Person> supplier = () -> new Person(); Supplier<Person> supplier1 = Person::new; System.out.println(supplier1.get()); } @Test public void test6(){ Function<Integer,Person> function = (x) -> new Person(x); Function<Integer,Person> function1 = Person::new; Person apply = function1.apply(15); System.out.println(apply); } //对象::实例方法 @Test public void test1(){ Consumer<String> con = (x) -> System.out.println(x); PrintStream out = System.out; Consumer<String> con1 = out::println; con1.accept("asdfs"); BiFunction<String,Integer,Person> biFunction = (x,y) -> new Person(x,y); BiFunction<String,Integer,Person> biFunction1 = Person::new; System.out.println(biFunction1.apply("小名",19)); } @Test public void test2() { Person person = new Person(); Supplier<String> supplier = () -> person.getUserName(); String s = supplier.get(); //对象::实例方法 Supplier<Integer> supplier1 = person::getAge; System.out.println(supplier1.get()); System.out.println(s); } //类::静态方法名 @Test public void test3(){ Comparator<Integer> com = (x,y) -> Integer.compare(x,y); Comparator<Integer> com1 = Integer::compare; } //类::实例方法名 @Test public void test4(){ BiPredicate<String,String> bp = (x,y) -> x.equals(y); BiPredicate<String,String> bp1 = String::equals; } }
3. 函数式接口 函数式接口(Functional Interface
)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。在接口上添加@FunctionalInterface
注解声明为函数时接口
函数式接口可以被隐式转换为 lambda
表达式。
1 2 3 4 5 @FunctionalInterface public interface MathOperation { Integer add(Integer a,Integer b); }
函数式接口可以对现有的函数友好地支持 lambda
。
JDK 1.8
新增加的函数接口:
java.util.function
它包含了很多类,用来支持 Java
的 函数式编程,该包中的函数式接口有: 序号| 接口 & 描述 —|— 1|Function<T,R> method: R apply(T t);
|接受一个输入参数,返回一个结果。 2|Consumer method: void accept(T t);
|代表了接受一个输入参数并且无返回的操作 3|Predicate method: boolean test(T t);
|接受一个输入参数,返回一个布尔值结果。 4|Supplier method : T get();
|无参数,返回一个结果。
函数式接口实例 Predicate <T>
接口是一个函数式接口,它接受一个输入参数 T,返回一个布尔值结果。
该接口包含多种默认方法来将Predicate
组合成其他复杂的逻辑(比如:与,或,非)。
该接口用于测试对象是 true 或 false。
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 /* Java 8 内置的四大核心函数式接口 Consumer<T> :消费型接口 void accept(T t); Supplier<T> : 供给型接口 T get(); Function<T,R> :函数式接口 R apply(T t); Predicate<T> :断言型接口 boolean test(T t); */ public class LambdaDemo6 { // Consumer<T> :消费型接口 @Test public void test1(){ happy(1000,(m)-> System.out.println("洗澡消费"+m+"元")); } public void happy(double money, Consumer<Double> con){ con.accept(money); } //Supplier<T> : 供给型接口 @Test public void test2(){ getNumList(10,() -> (int)( Math.random()*100)).forEach((m)-> System.out.println(m)); } //产生指定个数的整数,并放入集合中 public List<Integer> getNumList(int num, Supplier<Integer> supplier){ ArrayList<Integer> list = new ArrayList<>(); for (int i=0;i<num;i++){ Integer integer = supplier.get(); list.add(integer); } return list; } //Function<T,R> :函数式接口 @Test public void test3(){ String newStr = strHandler("\t\t\t\t 我爱张柏芝",(a)-> a.trim()); System.out.println(newStr); String subStr = strHandler("我喜欢你",(a)->a.substring(0,3)); System.out.println(subStr); } //需求:用于处理字符串 public String strHandler(String str, Function<String,String> function){ return function.apply(str); } //Predicate<T> :断言型接口 @Test public void test4(){ List<String> lists = Arrays.asList("Hello","atagui","Lambda","www","ok"); filterStr(lists,(s) -> s.length()>3).forEach((a)-> System.out.println(a)); } //将满足条件的字符串放入到集合中 public List<String> filterStr(List<String> list,Predicate<String> predicate) { ArrayList<String> strList = new ArrayList<>(); for (String str : list) { if (predicate.test(str)){ strList.add(str); } } return strList; } }
4. Java 8 默认方法 Java 8
新增了接口的默认方法。简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
为什么要有这个特性?
首先 ,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8
之前的集合框架没有 foreach
方法,通常能想到的解决办法是在JDK
里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
语法 1 2 3 4 5 public interface Vehicle { default void print(){ System.out.println("我是一辆车!"); } }
多个默认方法 一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法:
1 2 3 4 5 6 7 8 9 10 11 public interface Vehicle { default void print(){ System.out.println("我是一辆车!"); } } public interface FourWheeler { default void print(){ System.out.println("我是一辆四轮车!"); } }
第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:
1 2 3 4 5 public class Car implements Vehicle, FourWheeler { default void print(){ System.out.println("我是一辆四轮汽车!"); } }
第二种解决方案可以使用 super 来调用指定接口的默认方法:
1 2 3 4 5 public class Car implements Vehicle, FourWheeler { public void print(){ Vehicle.super.print(); } }
静态默认方法 Java 8
的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:
1 2 3 4 5 6 7 8 9 public interface Vehicle { default void print(){ System.out.println("我是一辆车!"); } // 静态方法 static void blowHorn(){ System.out.println("按喇叭!!!"); } }
5. Java 8 Stream Java 8 API
添加了一个新的抽象称为流Stream
,可以让你以一种声明的方式处理数据。
Stream
使用一种类似用 SQL
语句从数据库查询数据的直观方式来提供一种对Java
集合运算和表达的高阶抽象。
Stream API
可以极大提高Java
程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选 ,排序 ,聚合 等。
元素流在管道中经过中间操作(intermediate operation
)的处理,最后由最终操作(terminal operation
)得到前面处理的结果。
1 2 3 +--------------------+ +------+ +------+ +---+ +-------+ | stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect| +--------------------+ +------+ +------+ +---+ +-------+
什么是 Stream? Stream
(流)是一个来自数据源的元素队列并支持聚合操作
元素是特定类型的对象,形成一个队列。 Java
中的Stream
并不会存储元素,而是按需计算。
数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
聚合操作 类似SQL
语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection
操作不同, Stream
操作还有两个基础的特征:
Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代: 以前对集合遍历都是通过Iterator
或者For-Each
的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream
提供了内部迭代的方式, 通过访问者模式(Visitor
实现。
生成流 在Java 8
中, 集合接口有两个方法来生成流:
stream() − 为集合创建串行流。
parallelStream() − 为集合创建并行流。
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 /** * 一 Stream的三个步骤 * 1. 创建Stream流 * 2. 中间操作 * 3. 终止操作(终端操作) */ public class TestStreamAPI { //创建Stream @Test public void test1(){ //1. 可以通过Collection系列集合提供的stream() 或 parallelSteam() List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); //2.通过Arrays中的静态方法stream()获取数组流 Person[] persion = new Person[10]; Stream<Person> stream1 = Arrays.stream(persion); //3. 通过Stream类中的静态方法of() Stream<String> aaa = Stream.of("aaa", "bbb", "ccc"); //4. 创建无限流 //迭代 Stream<Integer> iterate = Stream.iterate(1, (x) -> x + 3); iterate.limit(10).forEach((x)-> System.out.println(x)); //生成 Stream.generate(()->(int)(Math.random()*100)).limit(10).forEach(System.out::println); } }
Stream API 中间操作 filter(),limit(),shkip(),distinct() 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 /** * 筛选与切片 * * filter---接收Lambda,从流中排除某些元素 * limit()---截断流,使其不超过给定数量 * skip()---跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补 * distinct--筛选,通过流所生成元素的hashcode()和equals()去除重复元素 */ @Test public void test3(){ list.stream() .filter((x)->x.getAge()>20) .skip(2) .distinct() .forEach(System.out::println); } @Test public void test2(){ list.stream() .filter((e)-> { System.out.println("短路"); return e.getAge()>20; }) .limit(2) .forEach(System.out::println); } //内部迭代:迭代操作有Stream API完成 @Test public void test1(){ //中间操作 Stream<Person> personStream = list.stream() .filter((e) -> { System.out.println("Stream API的中间操作"); return e.getAge() > 22; }); //终止操作:一次性执行全部内容,即"惰性求值" personStream.forEach(System.out::println); }
map() 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 /** * 映射 * map---接收Lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 *flatMap--- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 */ @Test public void test4(){ List<String> lists = Arrays.asList("aaa","bbb","ccc","ddd","eee"); lists.stream() .map((x)-> x.toUpperCase()) .forEach(System.out::println); System.out.println("----------------------"); list.stream() .map(Person::getUserName) .forEach(System.out::println); System.out.println("-----------------------"); Stream<Stream<Character>> stream = lists.stream() .map((x) -> TestStreamAPI2.filterCharacter(x)); stream.forEach((sm)-> sm.forEach(System.out::println) ); System.out.println("-----------------------"); //flatMap Stream<Character> characterStream = lists.stream() .flatMap(TestStreamAPI2::filterCharacter); characterStream.forEach(System.out::println); } public static Stream<Character> filterCharacter(String str){ List<Character> list1 = new ArrayList<>(); for (Character ch:str.toCharArray()) { list1.add(ch); } return list1.stream(); }
sorted(),sorted(Comparator com) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /** * 排序 * sorted()---自然排序 (Comparable) * sorted(Comparator com) ---定制排序(Comparator) */ @Test public void test6(){ List<String> lists = Arrays.asList("bbb","aaa","ddd","eee","ccc"); lists.stream() .sorted() .forEach(System.out::println); System.out.println("------------------------------"); list.stream() .sorted((e1,e2)-> e1.getAge().compareTo(e2.getAge())).forEach(System.out::println); }
终止操作(终端操作) 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 /** * 查找与匹配 * allMatch---检查是否匹配所有元素 * anyMatch---检查是否至少匹配一个元素 * noneMatch---检查是否没有匹配所有元素 * findFirst---返回第一个元素 * findAny---返回当前流中的任意元素 * count---放回流中元素的总个数 * max---返回流中最大值 * min---返回流中最小值 */ @Test public void test2(){ long count = list.stream() .count(); System.out.println(count); Optional<Person> max = list.stream() .max((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())); System.out.println(max.get()); Optional<Integer> min = list.stream() .map(Person::getAge) .min(Integer::compareTo); System.out.println(min.get()); } @Test public void test1(){ boolean b = list.stream() .allMatch((e) -> e.getStatus().equals(Status.BUSY)); System.out.println(b); boolean b1 = list.stream() .anyMatch((e) -> e.getStatus().equals(Status.BUSY)); System.out.println(b1); boolean b2 = list.stream() .noneMatch((e) -> e.getStatus().equals(Status.BUSY)); System.out.println(b2); Optional<Person> op = list.stream() .sorted((e1, e2) -> -Integer.compare(e1.getAge(), e2.getAge())) .findFirst(); //System.out.println(op.get()); //多线程查找 Optional<Person> op1 = list.parallelStream() .filter((e) -> e.getStatus().equals(Status.FREE)) .findAny(); System.out.println(op1.get()); }
reduce 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /**归约: * reduce(T identity, BinaryOperator<T> accumulator) * reduce(BinaryOperator<T> accumulator) * 可以将流中元素反复结合起来,得到一个值 */ @Test public void test3(){ List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list1.stream() .reduce(0, (x, y) -> x + y); System.out.println(sum); System.out.println("---------------------"); Optional<Integer> reduce = list.stream() .map(Person::getAge) .reduce(Integer::sum); System.out.println(reduce.get()); }
collect 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 97 98 99 100 101 102 103 104 105 106 107 /** * 收集 * collect---将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法 */ @Test public void test5(){ //总数 Long collect = list.stream() .collect(Collectors.counting()); System.out.println("总数:"+collect); //平均值 Double collect1 = list.stream() .collect(Collectors.averagingLong(Person::getAge)); System.out.println("平均值:"+collect1); //总和 Integer collect2 = list.stream() .collect(Collectors.summingInt(Person::getAge)); System.out.println("总和:"+collect2); //最大值 Optional<Person> collect3 = list.stream() .collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()))); System.out.println("最大值:"+collect3.get()); //最小值 Optional<Person> collect4 = list.stream() .collect(Collectors.minBy((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()))); System.out.println("最小值:"+collect4.get()); } @Test public void test4(){ List<String> collect = list.stream() .map(Person::getUserName) .collect(Collectors.toList()); //collect.forEach(System.out::println); Set<String> collect1 = list.stream() .map(Person::getUserName) .collect(Collectors.toSet()); collect1.forEach(System.out::println); Set<String> collect2 = list.stream() .map(Person::getUserName) .collect(Collectors.toCollection(HashSet::new)); } //分组 @Test public void test6(){ Map<Status, List<Person>> map = list.stream() .collect(Collectors.groupingBy(Person::getStatus)); System.out.println(map); } //多级分组 @Test public void test7(){ Map<Status, Map<String, List<Person>>> collect = list.stream() .collect(Collectors.groupingBy(Person::getStatus, Collectors.groupingBy( (e) -> { if (((Person) e).getAge() < 20) { return "高中生"; } else if (((Person) e).getAge() > 20) { return "大学生"; } else { return "成年人"; } } ))); System.out.println(collect); } //分区 满足条件一个区,不满足条件一个区 @Test public void test8(){ Map<Boolean, List<Person>> collect = list.stream() .collect(Collectors.partitioningBy((e) -> e.getAge() > 22)); System.out.println(collect); } @Test public void test10(){ String collect = list.stream() .map(Person::getUserName) .collect(Collectors.joining(",","===","===")); System.out.println(collect); } @Test public void test9(){ IntSummaryStatistics collect = list.stream() .collect(Collectors.summarizingInt(Person::getAge)); System.out.println(collect.getMax()); System.out.println(collect.getSum()); System.out.println(collect.getMin()); }
forEach Stream
提供了新的方法'forEach'
来迭代流中的每个数据。以下代码片段使用forEach
输出了10个随机数:
1 2 Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
map map
方法用于映射每个元素到对应的结果,以下代码片段使用 map
输出了元素对应的平方数:
1 2 3 List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 获取对应的平方数 List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter filter
方法用于通过设置的条件过滤出元素。以下代码片段使用 filter
方法过滤出空字符串:
1 2 3 List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 long count = strings.stream().filter(string -> string.isEmpty()).count();
limit limit
方法用于获取指定数量的流。 以下代码片段使用 limit
方法打印出 10 条数据:
1 2 Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
sorted sorted
方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:
1 2 Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);
并行(parallel)程序 parallelStream
是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:
1 2 3 List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
6. Optional类 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 public class TestOptional { //用于解决空指针异常 //Optional 容器类的常用方法 /**Optional.of(T t) :创建一个optional实例 * Optional.empty() : 创建一个空的Optional实例 * Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例 * isPresent(T t) : 判断是否包含值 * orElse(T t) : 如果调用对象包含值,返回该值,否则返回t * orElseGet(Supplier s) : 如果调用对象包含值,返回该值,否则返回s获取的值 * map(Function f) : 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty() * flatMap(Function mapper) : 与map类似,要求返回值必须是Optional */ @Test public void test5(){ Optional<Person> optionalPerson = Optional.ofNullable(new Person("韩信",21, Status.FREE)); Optional<String> s = optionalPerson.map(Person::getUserName); System.out.println(s.get()); Optional<String> s1 = optionalPerson.flatMap((e) -> Optional.of(e.getUserName())); System.out.println(s1); } @Test public void test4(){ Optional<Person> optionalPerson = Optional.ofNullable(new Person()); if (optionalPerson.isPresent()) System.out.println(optionalPerson.get()); Person person = optionalPerson.orElseGet(() -> new Person()); System.out.println(person); } @Test public void test3(){ Optional<Person> optionalPerson = Optional.ofNullable(new Person()); System.out.println(optionalPerson.get()); } @Test public void test2(){ Optional<Person> optionalPerson = Optional.empty(); System.out.println(optionalPerson.get()); } @Test public void test1(){ Optional<Person> person = Optional.of(new Person()); System.out.println(person.get()); } }
Date Time 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 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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 public class TestLocalDateTime { //DateTimeFormatter : 格式化时间/日期 @Test public void test6(){ DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE; LocalDateTime ldt = LocalDateTime.now(); String format = isoDateTime.format(ldt); System.out.println(format); System.out.println("---------------------------------"); DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); String format1 = dateTimeFormatter.format(ldt); System.out.println(format1); LocalDateTime parse = ldt.parse(format1,dateTimeFormatter); System.out.println(parse); } //TemporalAdjuster : 时间校正器 @Test public void test5(){ LocalDateTime ldt = LocalDateTime.now(); System.out.println(ldt); LocalDateTime ldt2 = ldt.withDayOfMonth(10); System.out.println(ldt2); LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); System.out.println(ldt3); //自定义 : 下一个工作日 ldt3.with((x) -> { LocalDateTime ldt4 = (LocalDateTime)x; DayOfWeek dow = ldt4.getDayOfWeek(); if (dow.equals(DayOfWeek.FRIDAY)){ return ldt4.plusDays(3); }else if (dow.equals(DayOfWeek.SATURDAY)){ return ldt4.plusDays(2); }else { return ldt4.plusDays(1); } }); } //3. Duration : 计算两个时间之间的间隔 //Period : 计算两个日期之间的间隔 @Test public void test4(){ LocalDate ld1 = LocalDate.of(2020, 1, 1); LocalDate ld2 = LocalDate.now(); Period period = Period.between(ld1,ld2); System.out.println(period); System.out.println("年:"+period.getYears()+"--月:"+period.getMonths()+"--日:"+period.getDays()+"--年代学:"+period.getChronology()+"--\n" + "\n" + "单位 :"+period.getUnits()); } @Test public void test3(){ //Duration : 计算两个时间之间的间隔 Instant now = Instant.now(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } Instant now1 = Instant.now(); Duration duration = Duration.between(now, now1); System.out.println(duration.toMillis()); } //2. Instant :时间戳(Unix 元年:1970 年1月1日 00:00:00 到某个时间之间的毫秒值) @Test public void test2(){ Instant now = Instant.now(); System.out.println(Instant.now()); OffsetDateTime offsetDateTime = Instant.now().atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime); System.out.println(now.toEpochMilli()); } //1. LocalTime LocalDate LocalDateTime @Test public void test1() { LocalDateTime now = LocalDateTime.now(); System.out.println(now); LocalDateTime of = LocalDateTime.of(2020, 4, 17, 16, 40); System.out.println(of); } }