作为一名Java开发者,JDK是其必备的开发工具,如今JDK已经更新到了JDK11,可是最近我发现很多有过多年Java开发经验的开发者,居然还在使用JDK6或者更早的版本,或者使用了JDK8,但是对JDK8及其之后版本的一些新特性完全不了解,要知道JDK8可是在2013年就正式发布了。小编自从尝试了JDK8带来的快感后,对于每次JDK新版本的发布,总是抱有很大的期待,虽然每次JDK版本的正式发布,总是带来不少的新特性和重要改进,不过遗憾的是,JDK8之后版本的新特性对于一个着重Java应用开发的开发者来说感觉并不明显,感觉最明显的还是JDK8,引入的Lambda表达式和Stream编程。
一、Lambda表达式
Lambda表达式简单理解即函数式编程,其并非Java独有,很多编程语言都有,如python、c++。 在JDK8之前,Java是不支持函数式编程的,所谓的函数编程,即可理解是将一个函数(也称为“行为”)作为一个参数进行传递。通常我们提及得更多的是面向对象编程,面向对象编程是对数据的抽象,而函数式编程则是对行为的抽象(将行为作为一个参数进行传递)。 Java 中的 Lambda 表达式通常使用 (argument) -> (expression) 语法书写,例如:
(arg1, arg2...) -> { expression }
(type1 arg1, type2 arg2...) -> { expression }
以下是一些 Lambda 表达式的例子:
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello 2019");
(String s) -> { System.out.println(s); }
() -> 30
() -> { return 4 };
通过上面的例子,可以简单了解一下 Lambda 表达式的结构:
1,一个 Lambda 表达式可以有零个或多个参数;
2,参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同。
3,参数需包含在圆括号内,参数之间用逗号分隔。例如:(a, b) 或 (String a, String b) 或 (int a, String b, float c),空圆括号代表参数集为空。例如:() -> 30;当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a;
4,Lambda 表达式的主体({}内的表达式)可包含零条或多条语句,如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中。下面通过代码来举几个简单例子。
1,线程的初始化方法
//旧方法:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("this thread");
}
}).start();
//新方法
new Thread(
() -> System.out.println("this thread")
).start();
2,List循环转化为Lambda表达式
//老方法
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
System.out.println(n);
}
//新方法
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
或者
list.forEach(System.out::println);
3,对象排序中比较器的实现
//老方法
Comparator<Developer> byName = new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getName().compareTo(o2.getName());
}
};
//新方法
Comparator<Developer> byName =
(Developer o1, Developer o2)->o1.getName().compareTo(o2.getName());
二、Stream API
Stream API作为 JDK8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX(Streaming API for XML) 对 XML 解析的 Stream,也不是Storm、Spark、Flink等对大数据实时处理的 Stream。JDK8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式(JDK7引入)来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,JDK8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。那JDK8中的Stream是什么?Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Fork/Join 框架来拆分任务和加速处理过程。下面通过几个实列来了解Stream API的用法。
1,map/flatMap
//老方法
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums=new ArrayList<>();
for(Integer num:nums)
{
squareNums.add(num*num);
}
//新方法
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums = nums.stream().map(n -> n * n).collect(Collectors.toList());// map 生成的是个一对一映射,每个输入元素,都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时可以使用 flatMap,有兴趣的可以参见后文列举的参考资料,这里不详述。
2,filter
//老方法
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
List<Integer> evensList = new ArrayList<>();
for(Integer num:sixNums)
{if(num%2==0){evens.add(num);}
}
Integer[] evens= new Integer[evensList .size()];
evens = evensList .toArray(evens);
//新方法
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens =
Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);
3,forEach
见前文Lambda表达式。
4,findFirst
它总是返回 Stream 的第一个元素,或者空。这里比较重点的是它的返回值类型:Optional,它可能含有某值,或者不包含。使用它的目的是尽可能避免NullPointerException。
如String strA = " abcd ", strB = null;要求求strA或者strB的长度,即实现
getLength(String text)方法。如下:
public static int getLength(String text) {
// 老方法
// return if (text != null) ? text.length() : -1;
//新方法
return Optional.ofNullable(text).map(String::length).orElse(-1);
};
5,limit/skip
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素。代码如下:
public void testLimitAndSkip() {
List<Person> persons = new ArrayList();
for (int i = 1; i <= 10000; i++) {
Person person = new Person(i, "name" + i);
persons.add(person);
}
List<String> personList2 = persons.stream().map(Person::getName).limit(10).skip(3).collect(Collectors.toList());
System.out.println(personList2);
}
private class Person {
public int no;
private String name;
public Person (int no, String name) {
this.no = no;
this.name = name;
}
public String getName() {
return name;
}
}
输出结果:
[name4, name5, name6, name7, name8, name9, name10]
6,stored
对 Stream 的排序通过 sorted 进行,它比数组的排序更强之处在于你可以首先对 Stream 进行各类 map、filter、limit、skip 甚至 distinct 来减少元素数量后,再排序,这能帮助程序明显缩短执行时间。
List<Person> persons = new ArrayList();
for (int i = 1; i <= 5; i++) {
Person person = new Person(i, "name" + i);
persons.add(person);
}
List<Person> personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());
System.out.println(personList2);
7,Match
Stream 有三个 match 方法,从语义上说:
allMatch:Stream 中全部元素符合传入的 predicate,返回 true
anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
它们都不是要遍历全部元素才能返回结果。例如 allMatch 只要一个元素不满足条件,就 skip 剩下的所有元素,返回 false。如:
List<Person> persons = new ArrayList();
persons.add(new Person(1, "name" + 1, 10));
persons.add(new Person(2, "name" + 2, 21));
persons.add(new Person(3, "name" + 3, 34));
persons.add(new Person(4, "name" + 4, 6));
persons.add(new Person(5, "name" + 5, 55));
boolean isAllAdult = persons.stream().
allMatch(p -> p.getAge() > 18);
System.out.println("All are adult? " + isAllAdult);
boolean isThereAnyChild = persons.stream().
anyMatch(p -> p.getAge() < 12);
System.out.println("Any child? " + isThereAnyChild);
输出结果:
All are adult? false
Any child? true
本文是总结自己的工作实战,抛砖引玉,列举了常见的JDK8 Lambda表达式和Stream API。如果想深入了解JDK8的特性,可以参看Oracle和IBM官网资料。
1,https://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html
2,https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/