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();
}

很显然,这个并不是一个很简洁的写法,我们采用Java8Lambada表达式来实现,那么如何简化呢?

整个过程:去掉修饰符(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体的操作已经有实现的方法了,可以使用方法引用

语法:使用操作符 “ ::” 将方法名和对象或类的名字分隔开来

几种常见形式:

  • 类名::静态方法
  • 对象::实例方法
  • 类名::实例方法

二.构造器引用

  • ClassName::new

三 数组引用

  • Type::new

注意:

  • 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.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);



}
}