Java 8的6个问题
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 表达式的缺点
1
2
3
4
|
List lengths = new ArrayList();
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
|
public interface TimeClient {
// ...
static public ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println( "Invalid time zone: " + zoneString +
"; using default time zone instead." );
return ZoneId.systemDefault();
}
}
default public ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}
|
就是这样,问题迎刃而解了。是这样么?Default方法将接口和实现分离混合了。似乎我们不用再纠结他们本身的分层结构了,现在我们需要解决新的问题了。想要了解更多,阅读Oleg Shelajev在RebelLabs上发表的文章吧。
4. 该如何拯救你,Jagsaw?
但是,它在哪呢?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个问题相关推荐
- springboot实现SSE服务端主动向客户端推送数据,java服务端向客户端推送数据,kotlin模拟客户端向服务端推送数据
SSE服务端推送 服务器向浏览器推送信息,除了 WebSocket,还有一种方法:Server-Sent Events(以下简称 SSE).本文介绍它的用法. 在很多业务场景中,会涉及到服务端向客户端 ...
- Java 获取当前时间之后的第一个周几,java获取当前日期的下一个周几
Java 获取当前时间之后的第一个周几,java获取当前日期的下一个周几 //获得入参的日期 Calendar cd = Calendar.getInstance(); cd.setTime(date ...
- 在k8s中使用gradle构建java web项目镜像Dockerfile
在k8s中使用gradle构建java web项目镜像Dockerfile FROM gradle:6-jdk8 AS build COPY --chown=gradle:gradle . /home ...
- Java | kotlin 手动注入bean,解决lateinit property loginService has not been initialized异常
kotlin.UninitializedPropertyAccessException: lateinit property loginService has not been initialized ...
- SpringBoot项目使用nacos,kotlin使用nacos,java项目使用nacos,gradle项目使用nacos,maven项目使用nacos
SpringBoot项目使用nacos kotlin demo见Gitte 一.引入依赖 提示:这里推荐使用2.2.3版本,springboot与nacos的依赖需要版本相同,否则会报错. maven ...
- OpenAPI使用(swagger3),Kotlin使用swagger3,Java使用swagger3,gradle、Maven使用swagger3
OpenAPI使用(swagger3) demo见Gitte 一.背景及名词解释 OpenAPI是规范的正式名称.规范的开发工作于2015年启动,当时SmartBear(负责Swagger工具开发的公 ...
- Gradle错误提示:Java home supplied via ‘xxx.xxx.xxx‘ is invalid
Gradle错误提示:Java home supplied via 'org.gradle.java.home' is invalid 描述:在使用idea采用gradle进行依赖的管理功能,当想切换 ...
- 查看Hotspot源码,查看java各个版本源码的网站,如何查看jdk源码
java开发必知必会之看源码,而看源码的第一步则是找到源码
- java基本类型转换,随记
java基本类型转换: double double 转 long double random = Math.round(Math.random()*10000); long l = new Doubl ...
- 科学处理java.lang.StackOverflowError: null异常
java.lang.StackOverflowError: null异常处理 在项目运行中出现StackOverflowError 首先要检查在编码中是否有明显的递归编码,比如死循环或者无限循环调用. ...
最新文章
- 前端开发中的性能那点事
- (转)ASP.NET-关于Container dataitem 与 eval方法介绍
- xbox360 功率测试软件,【外星人 Alpha ASM100-1580 游戏主机使用总结】性能|电压|功耗|跑分_摘要频道_什么值得买...
- 【APICloud系列|35】小米应用商店版本更新
- git报错 ssh: Could not resolve hostname gitee.com:xxxxxx: Name or service not known fatal
- python小球弹跳_python实现小球弹跳效果
- Android开发:ZXing条码扫描-竖屏解决方案
- FPGA工程师面试试题集锦11~20
- Play 1.x 学习资料汇总
- 小米 MIUI系统 安装包 刷机
- mc服务器称号显示插件,[聊天]UDtitle ——称号管理(仓库) 1.1.6 [全版本]
- 第26课:个人高效的秘籍 OKR 工作法
- Linux中的阻塞机制
- OkHttpUtils | okhttp-OkGo的使用,完美支持RxJava
- Kafka工具--滴滴Logi-KafkaManager
- JZOJ3918 蛋糕
- php设备在线报修,php企业设备报修系统
- All Eyes on Docs! 练就火眼金睛,就来StarRocks 极客营
- SRCNN超分辨率Pytorch实现,代码逐行讲解,附源码
- 概率论基础 —— 4.五种重要的概率分布模型
热门文章
- 人一样自然流畅地说话,下一代智能对话系统还有多长的路要走?
- 让Elasticsearch飞起来!——性能优化实践干货
- Kaggle 数据挖掘比赛经验分享 (转载)
- 华为将操作系统“装入”海尔
- tomcat php模块化运行,Windows下 Tomcat 运行 PHP 的配置
- Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...
- 怎么解决线上CPU100%的问题
- python删除排序数组中的重复项
- LtScrollImageView:自动滚动的广告图片展示栏
- 射影几何笔记4:证明的思路