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/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容