Java的性能可选
我只是偶然发现了Java 8中的Optional类 – 我真的喜欢在我的代码中使用isPresent()方法调用替换一些空检查(字面意思是“值是否存在?”)的方法。
我的问题是:这不会导致我的代码性能下降吗? 我只是猜测简单的空检查可能会有点便宜,而且我在字节码读取/解释方面还不是很好,所以我真的对你对这个主题的想法很感兴趣。
Optional
只是一个普通的generics类,它包含类型为T的引用。因此,它添加了一个间接层。 方法调用本身也不会非常昂贵,因为类是final
,因此可以避免动态调度。
你可能遇到性能问题的唯一地方是使用大量此类实例时,但即使这样, Stream
类的性能也一点都不差。 但是,在处理大量原始值时,使用Stream
(或Integer[]
)与原始特化IntStream
(或int[]
)会发现性能IntStream
因为这个间接层需要非常频繁的实例化Integer
对象。 但是,这是我们已经知道并在使用ArrayList
类的东西时付出的代价。
您显然会遇到与Stream
/ OptionalInt[]
相同的命中,因为OptionalInt基本上是一个带有int
字段和boolean
标志的类(与Optional
不同,它只能与T
字段一起使用) )因此与Integer
非常相似,但尺寸更大。 当然, Stream
会添加两个间接级别,相应的双重性能损失。
我使用大量使用空检查的算法以及对可能为空的字段的访问进行了一些性能测试。 我实现了一个简单的算法,从单个链表中删除中间元素。
首先,我实现了两类链表节点:safe – with Optional和unsafe – without。
安全节点
class Node { private final T data; private Optional> next = Optional.empty(); Node(T data) { this.data = data; } Optional> getNext() { return next; } void setNext(Node next) { setNext(Optional.ofNullable(next)); } void setNext(Optional> next ) { this.next = next; } }
不安全的节点
class NodeUnsafe { private final T data; private NodeUnsafe next; NodeUnsafe(T data) { this.data = data; } NodeUnsafe getNext() { return next; } void setNext(NodeUnsafe next) { this.next = next; } }
然后我实现了两个相似的方法,唯一的区别是 – 第一个使用Node
,第二个使用NodeUsafe
类DeleteMiddle {
private static T getLinkedList(int size, Function supplier, BiConsumer reducer) { T head = supplier.apply(1); IntStream.rangeClosed(2, size).mapToObj(supplier::apply).reduce(head,(a,b)->{ reducer.accept(a,b); return b; }); return head; } private static void deleteMiddle(Node head){ Optional> oneStep = Optional.of(head); Optional> doubleStep = oneStep; Optional> prevStep = Optional.empty(); while (doubleStep.isPresent() && doubleStep.get().getNext().isPresent()){ doubleStep = doubleStep.get().getNext().get().getNext(); prevStep = oneStep; oneStep = oneStep.get().getNext(); } final Optional> toDelete = oneStep; prevStep.ifPresent(s->s.setNext(toDelete.flatMap(Node::getNext))); } private static void deleteMiddleUnsafe(NodeUnsafe head){ NodeUnsafe oneStep = head; NodeUnsafe doubleStep = oneStep; NodeUnsafe prevStep = null; while (doubleStep != null && doubleStep.getNext() != null){ doubleStep = doubleStep.getNext().getNext(); prevStep = oneStep; oneStep = oneStep.getNext(); } if (prevStep != null) { prevStep.setNext(oneStep.getNext()); } } public static void main(String[] args) { int size = 10000000; Node head = getLinkedList(size, Node::new, Node::setNext); Long before = System.currentTimeMillis(); deleteMiddle(head); System.out.println("Safe: " +(System.currentTimeMillis() - before)); NodeUnsafe headUnsafe = getLinkedList(size, NodeUnsafe::new, NodeUnsafe::setNext); before = System.currentTimeMillis(); deleteMiddleUnsafe(headUnsafe); System.out.println("Unsafe: " +(System.currentTimeMillis() - before)); } }
使用不同大小的列表进行的两次运行的比较表明,使用Optional
代码最好的方法比使用nullables的方法快两倍 。 使用小列表它慢3倍。
我们使用openjdk标记下面的代码。
sc.map(MYObject::getRequest) .map(RequestDO::getMyInst) .map(MyInstDO::getCar) .map(CarDO::getId); if(id.isPresent())
要么
if( null != MYObject.getRequest() && null != MYObject.getRequest().getMyInst() && null != MYObject.getRequest().getMyInst().getCar() && null != MYObject.getRequest().getMyInst().getCar().getId() )
结果显示Optional比传统的非null检查要好得多。
Benchmark Mode Cnt Score Error Units JMHBMarkModes.measureNotNull thrpt 5 0.149 ± 0.036 ops/us JMHBMarkModes.measureOptional thrpt 5 11.418 ± 1.140 ops/us JMHBMarkModes.measureNotNull avgt 5 12.342 ± 8.334 us/op JMHBMarkModes.measureOptional avgt 5 0.088 ± 0.010 us/op
但是如果你的用例是(null != MYObject.getRequest())
,那么不是null检查更好。 所以可选性能取决于您的用例。