Java开发者还不了解这两点技术,你的饭碗可要小心了

        作为一名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/

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容