我只是偶然发现了Java 8中的Optional类 - 我真的喜欢在我的代码中使用isPresent()方法调用替换一些空检查(字面意思是"值是否存在?")的方法.
我的问题是:这不会导致我的代码性能下降吗?我只是猜测简单的空检查可能会有点便宜而且我在字节码读取/解释方面还不是很好,所以我真的对你对这个主题的想法很感兴趣.
Optional
它只是一个普通的泛型类,它包含一个类型为T的引用.因此,它添加了一个单向的间接层.方法调用本身也不会非常昂贵,因为类是final
,因此可以避免动态调度.
你可能遇到性能问题的唯一地方就是在处理大量此类实例时,但即便如此,像a这样的东西的表现也一点Stream
都不差.然而,随着大量的原始值的工作时,你会发现使用性能损失Stream
(或Integer[]
)与原始专业化IntStream
(或int[]
)由于这层间接的,需要的非常频繁实例化Integer
对象.然而,这是我们已经知道并且在使用类似物时付出的代价ArrayList
.
您显然会遇到与Stream
/ 相同的命中OptionalInt[]
,因为OptionalInt基本上是一个带有int
字段和存在boolean
标志的类(与Optional
仅使用T
字段的情况不同)因此非常相似,Integer
尽管尺寸更大.当然,a 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 {
class DeleteMiddle { private staticT 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倍.