1. 并行Streams实际上可能会降低你的性能

Java8带来了最让人期待的新特性之–并行。parallelStream() 方法在集合和流上实现了并行。它将它们分解成子问题,然后分配给不同的线程进行处理,这些任务可以分给不同的CPU核心处理,完成后再合并到一起。实现原理主要是使用了fork/join框架。好吧,听起来很酷对吧!那一定可以在多核环境下使得操作大数据集合速度加快咯,对吗?

不,如果使用不正确的话实际上会使得你的代码运行的更慢。我们进行了一些基准测试,发现要慢15%,甚至可能更糟糕。假设我们已经运行了多个线程,然后使用.parallelStream() 来增加更多的线程到线程池中,这很容易就超过多核心CPU处理的上限,从而增加了上下文切换次数,使得整体都变慢了。

基准测试将一个集合分成不同的组(主要/非主要的):

1
2
Map<Boolean, List<Integer>> groupByPrimary = numbers
.parallelStream().collect(Collectors.groupingBy(s -> Utility.isPrime(s)));

使得性能降低也有可能是其他的原因。假如我们分成多个任务来处理,其中一个任务可能因为某些原因使得处理时间比其他的任务长很多。.parallelStream() 将任务分解处理,可能要比作为一个完整的任务处理要慢。来看看这篇文章, Lukas Krecan给出的一些例子和代码 。

提醒:并行带来了很多好处,但是同样也会有一些其他的问题需要考虑到。当你已经在多线程环境中运行了,记住这点,自己要熟悉背后的运行机制。

2. Lambda 表达式的缺点

lambda表达式。哦,lambda表达式。没有lambda表达式我们也能做到几乎一切事情,但是lambda是那么的优雅,摆脱了烦人的代码,所以很容易就爱上lambda。比如说早上起来我想遍历世界杯的球员名单并且知道具体的人数(有趣的事实:加起来有254个)。
1
2
3
4
List lengths = newArrayList();
for(String countries : Arrays.asList(args)) {
    lengths.add(check(country));
}

现在我们用一个漂亮的lambda表达式来实现同样的功能:

1
Stream lengths = countries.stream().map(countries -< check(country));

哇塞!这真是超级厉害。增加一些像lambda表达式这样的新元素到Java当中,尽管看起来更像是一件好事,但是实际上却是偏离了Java原本的规范。字节码是完全面向对象的,伴随着lambda的加入 ,这使得实际的代码与运行时的字节码结构上差异变大。关于lambda表达式的负面影响可以看Tal Weiss这篇文章。

从更深层次来看,你写什么代码和调试什么代码是两码事。堆栈跟踪越来越大,使得难以调试代码。一些很简单的事情譬如添加一个空字符串到list中,本来是这样一个很短的堆栈跟踪

1
2
at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.main(LmbdaMain.java:34)

变成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.lambda$0(LmbdaMain.java:37)
at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
at LmbdaMain.main(LmbdaMain.java:39

lambda表达式带来的另一个问题是关于重载:使用他们调用一个方法时会有一些传参,这些参数可能是多种类型的,这样会使得在某些情况下导致一些引起歧义的调用。Lukas Eder 用示例代码进行了说明。

提醒:要意识到这一点,跟踪有时候可能会很痛苦,但是这不足以让我们远离宝贵的lambda表达式。

3. Default方法令人分心

Default方法允许一个功能接口中有一个默认实现,这无疑是Java8新特性中最酷的一个,但是它与我们之前使用的方式有些冲突。那么既然如此,为什么要引入default方法呢?如果不引入呢?

Defalut方法背后的主要动机是,如果我们要给现有的接口增加一个方法,我们可以不用重写实现来达到这个目的,并且使它与旧版本兼容。例如,拿这段来自Oracle Java教程中 添加指定一个时区功能的代码来说:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
publicinterface TimeClient {
// ...
staticpublic ZoneId getZoneId (String zoneString) {
try{
    returnZoneId.of(zoneString);
}catch(DateTimeException e) {
    System.err.println("Invalid time zone: " + zoneString +
    "; using default time zone instead.");
    returnZoneId.systemDefault();
    }
}
defaultpublic ZonedDateTime getZonedDateTime(String zoneString) {
    returnZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }
}

就是这样,问题迎刃而解了。是这样么?Default方法将接口和实现分离混合了。似乎我们不用再纠结他们本身的分层结构了,现在我们需要解决新的问题了。想要了解更多,阅读Oleg Shelajev在RebelLabs上发表的文章吧。

提醒:当你手上有一把锤子的时候,看什么都像是钉子。记住它们原本的用法,保持原来的接口而重构引入新的抽象类是没有意义的。

4. 该如何拯救你,Jagsaw?

Jigsaw项目的目标是使Java模块化,将JRE分拆成可以相互操作的组件。这背后最主要的动机是渴望有一个更好、更快、更强大的Java嵌入式。我试图避免提及“物联网”,但我还是说了。减少JAR的体积,改进性能,增强安全性等等是这个雄心勃勃的项目所承诺的。

但是,它在哪呢?Oracle的首席Java架构师, Mark Reinhold说:  Jigsaw,通过了探索阶段 ,最近才进入第二阶段,现在开始进行产品的设计与实现。该项目原本计划在Java8完成。现在推迟到Java9,有可能成为其最主要的新特性。

提醒:如果这正是你在等待的, Java9应该在2016年间发布。同时,想要密切关注甚至参与其中的话,你可以加入到这个邮件列表。

5. 那些仍然存在的问题

受检异常

没有人喜欢繁琐的代码,那也是为什么lambdas表达式那么受欢迎的的原因。想想讨厌的异常,无论你是否需要在逻辑上catch或者要处理受检异常,你都需要catch它们。即使有些永远也不会发生,像下面这个异常就是永远也不会发生的:

1
2
3
try{
    httpConn.setRequestMethod("GET");
} catch(ProtocolException pe) { /* Why don’t you call me anymore? */ }

原始类型

它们依然还在,想要正确使用它们是一件很痛苦的事情。原始类型导致Java没能够成为一种纯面向对象语言,而移除它们对性能也没有显著的影响。顺便提一句,新的JVM语言都没有包含原始类型。

运算符重载

James Gosling,Java之父,曾经在接受采访时说:“我抛弃运算符重载是因为我个人主观的原因,因为在C++中我见过太多的人在滥用它。”有道理,但是很多人持不同的观点。其他的JVM语言也提供这一功能,但是另一方面,它导致有些代码像下面这样:

1
2
3
javascriptEntryPoints <<= (sourceDirectory in Compile)(base =>
    ((base / "assets"** "*.js") --- (base / "assets"** "_*")).get
)

事实上这行代码来自Scala  Play框架,我现在都有点晕了。

提醒:这些是真正的问题么?我们都有自己的怪癖,而这些就是Java的怪癖。在未来的版本中可能有会发生一些意外,它将会改变,但向后兼容性等等使得它们现在还在使用。

6. 函数式编程–为时尚早

函数式编程出现在java之前,但是它相当的尴尬。Java8在这方面有所改善例如lambdas等等。这是让人受欢迎的,但却不如早期所描绘的那样变化巨大。肯定比Java7更优雅,但是仍需要努力增加一些真正需要的功能。

其中一个在这个问题上最激烈的评论来自Pierre-yves Saumont,他写了一系列的文章详细的讲述了函数式编程规范和其在Java中实现的差异。

所以,选择Java还是Scala呢?Java采用现代函数范式是对使用多年Lambda的Scala的一种肯定。Lambdas让我们觉得很迷惑,但是也有许多像traits,lazy evaluation和immutables等一些特性,使得它们相当的不同。

提醒:不要为lambdas分心,在Java8中使用函数式编程仍然是比较麻烦的。

原文链接:  Jaxenter  翻译:  ImportNew.com  -  光光头去打酱油

译文链接: http://www.importnew.com/13972.html

from: http://www.importnew.com/13972.html

本文由 ImportNew - 光光头去打酱油 翻译自 Jaxenter。

Java 8的6个问题相关推荐

  1. springboot实现SSE服务端主动向客户端推送数据,java服务端向客户端推送数据,kotlin模拟客户端向服务端推送数据

    SSE服务端推送 服务器向浏览器推送信息,除了 WebSocket,还有一种方法:Server-Sent Events(以下简称 SSE).本文介绍它的用法. 在很多业务场景中,会涉及到服务端向客户端 ...

  2. Java 获取当前时间之后的第一个周几,java获取当前日期的下一个周几

    Java 获取当前时间之后的第一个周几,java获取当前日期的下一个周几 //获得入参的日期 Calendar cd = Calendar.getInstance(); cd.setTime(date ...

  3. 在k8s中使用gradle构建java web项目镜像Dockerfile

    在k8s中使用gradle构建java web项目镜像Dockerfile FROM gradle:6-jdk8 AS build COPY --chown=gradle:gradle . /home ...

  4. Java | kotlin 手动注入bean,解决lateinit property loginService has not been initialized异常

    kotlin.UninitializedPropertyAccessException: lateinit property loginService has not been initialized ...

  5. SpringBoot项目使用nacos,kotlin使用nacos,java项目使用nacos,gradle项目使用nacos,maven项目使用nacos

    SpringBoot项目使用nacos kotlin demo见Gitte 一.引入依赖 提示:这里推荐使用2.2.3版本,springboot与nacos的依赖需要版本相同,否则会报错. maven ...

  6. OpenAPI使用(swagger3),Kotlin使用swagger3,Java使用swagger3,gradle、Maven使用swagger3

    OpenAPI使用(swagger3) demo见Gitte 一.背景及名词解释 OpenAPI是规范的正式名称.规范的开发工作于2015年启动,当时SmartBear(负责Swagger工具开发的公 ...

  7. Gradle错误提示:Java home supplied via ‘xxx.xxx.xxx‘ is invalid

    Gradle错误提示:Java home supplied via 'org.gradle.java.home' is invalid 描述:在使用idea采用gradle进行依赖的管理功能,当想切换 ...

  8. 查看Hotspot源码,查看java各个版本源码的网站,如何查看jdk源码

    java开发必知必会之看源码,而看源码的第一步则是找到源码

  9. java基本类型转换,随记

    java基本类型转换: double double 转 long double random = Math.round(Math.random()*10000); long l = new Doubl ...

  10. 科学处理java.lang.StackOverflowError: null异常

    java.lang.StackOverflowError: null异常处理 在项目运行中出现StackOverflowError 首先要检查在编码中是否有明显的递归编码,比如死循环或者无限循环调用. ...

最新文章

  1. 前端开发中的性能那点事
  2. (转)ASP.NET-关于Container dataitem 与 eval方法介绍
  3. xbox360 功率测试软件,【外星人 Alpha ASM100-1580 游戏主机使用总结】性能|电压|功耗|跑分_摘要频道_什么值得买...
  4. 【APICloud系列|35】小米应用商店版本更新
  5. git报错 ssh: Could not resolve hostname gitee.com:xxxxxx: Name or service not known fatal
  6. python小球弹跳_python实现小球弹跳效果
  7. Android开发:ZXing条码扫描-竖屏解决方案
  8. FPGA工程师面试试题集锦11~20
  9. Play 1.x 学习资料汇总
  10. 小米 MIUI系统 安装包 刷机
  11. mc服务器称号显示插件,[聊天]UDtitle ——称号管理(仓库) 1.1.6 [全版本]
  12. 第26课:个人高效的秘籍 OKR 工作法
  13. Linux中的阻塞机制
  14. OkHttpUtils | okhttp-OkGo的使用,完美支持RxJava
  15. Kafka工具--滴滴Logi-KafkaManager
  16. JZOJ3918 蛋糕
  17. php设备在线报修,php企业设备报修系统
  18. All Eyes on Docs! 练就火眼金睛,就来StarRocks 极客营
  19. SRCNN超分辨率Pytorch实现,代码逐行讲解,附源码
  20. 概率论基础 —— 4.五种重要的概率分布模型

热门文章

  1. 人一样自然流畅地说话,下一代智能对话系统还有多长的路要走?
  2. 让Elasticsearch飞起来!——性能优化实践干货
  3. Kaggle 数据挖掘比赛经验分享 (转载)
  4. 华为将操作系统“装入”海尔
  5. tomcat php模块化运行,Windows下 Tomcat 运行 PHP 的配置
  6. Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...
  7. 怎么解决线上CPU100%的问题
  8. python删除排序数组中的重复项
  9. LtScrollImageView:自动滚动的广告图片展示栏
  10. 射影几何笔记4:证明的思路