2016-01-09 20 views
9

Tôi vừa vấp vào lớp Tùy chọn trong Java 8 - Tôi thực sự thích cách thay thế một số kiểm tra rỗng (nghĩa đen là "là giá trị hiện tại?") Trong mã của tôi với phương thức isPresent() cuộc gọi.Hiệu suất của Java Tùy chọn

Câu hỏi của tôi là: Điều đó có gây ra hiệu suất mã thấp hơn không? Tôi chỉ đoán kiểm tra null đơn giản có thể rẻ hơn một chút và tôi chưa giỏi đọc mã/giải thích byte, vì vậy tôi thực sự quan tâm đến suy nghĩ của bạn về chủ đề đó.

+3

Tại sao bạn không đánh giá nó? –

+2

bạn không nên làm 'isPresent', thay vào đó sử dụng' map' và 'orElse'. –

+1

@ Łukasz: Điều đó cần biện minh. Đôi khi nó đúng, nhưng nếu bạn muốn thực hiện tác vụ phụ khi giá trị có mặt, bạn sẽ làm gì nếu không 'if (isPresent()) doSomething()'? Cả bản đồ lẫn orElse đều không có ý nghĩa ở đó. –

Trả lời

11

Optional<T> chỉ là một lớp chung chung bình thường có chứa tham chiếu loại T. Vì vậy, nó thêm một lớp đơn lẻ. Phương thức tự gọi mình cũng sẽ không tốn kém lắm, vì lớp học là final và do đó có thể tránh được công văn động.

Nơi duy nhất bạn có thể có vấn đề về hiệu suất là khi làm việc với số lượng rất lớn các trường hợp như vậy, nhưng ngay cả khi đó hiệu suất của một số thứ như Stream<Optional<String>> không hề tệ chút nào. Tuy nhiên, khi làm việc với số lượng lớn giá trị nguyên thủy, bạn sẽ tìm thấy hiệu suất đạt được bằng cách sử dụng Stream<Integer> (hoặc Integer[]) so với chuyên môn nguyên thủy IntStream (hoặc int[]) do lớp này của yêu cầu đòi hỏi phải có sự xuất hiện rất thường xuyên của các đối tượng Integer. Tuy nhiên, đây là một hình phạt mà chúng tôi đã biết và trả tiền khi sử dụng những thứ như ArrayList<Integer>.

Bạn rõ ràng sẽ trải nghiệm hit cùng với Stream<OptionalInt>/OptionalInt[], vì một OptionalInt về cơ bản là một lớp học với một trường int và một lá cờ boolean presence (không giống với Optional<T> mà có thể làm gì với chỉ lĩnh vực T) và do đó khá tương tự như Integer mặc dù kích thước lớn hơn. Và tất nhiên, một số Stream<Optional<Integer>> sẽ thêm hai mức mức vô hướng, với tỷ lệ phạt hiệu suất kép tương ứng.

0

Băng ghế dự bị được đánh dấu mã bên dưới bằng cách sử dụng openjdk.

sc.map(MYObject::getRequest) 
    .map(RequestDO::getMyInst) 
    .map(MyInstDO::getCar) 
    .map(CarDO::getId); 

if(id.isPresent()) 

HOẶC

if(null != MYObject.getRequest() && null != 
    MYObject.getRequest().getMyInst() && null != 
    MYObject.getRequest().getMyInst().getCar() && null != 
    MYObject.getRequest().getMyInst().getCar().getId()) 

Và kết quả cho thấy tùy chọn là tốt hơn nhiều so truyền thống kiểm tra không 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 

Nhưng nếu trường hợp sử dụng của bạn giống như (null != MYObject.getRequest()), thì không được kiểm tra tốt hơn. Vì vậy, hiệu suất tùy chọn phụ thuộc vào trường hợp sử dụng bạn có.

5

Tôi đã thực hiện một số thử nghiệm hiệu suất bằng cách sử dụng thuật toán sử dụng rất nhiều kiểm tra null cũng như quyền truy cập vào trường có khả năng không có giá trị. Tôi đã triển khai một thuật toán đơn giản loại bỏ phần tử ở giữa khỏi danh sách liên kết đơn lẻ.

Trước tiên, tôi đã triển khai hai lớp nút danh sách được liên kết: an toàn - với tùy chọn và không an toàn - không có.

Node Safe

class Node<T> { 
    private final T data; 
    private Optional<Node<T>> next = Optional.empty(); 

    Node(T data) { 

     this.data = data; 
    } 

    Optional<Node<T>> getNext() { 
     return next; 
    } 

    void setNext(Node<T> next) { setNext(Optional.ofNullable(next)); } 

    void setNext(Optional<Node<T>> next) { this.next = next; } 
} 

Node không an toàn

class NodeUnsafe<T> { 
    private final T data; 
    private NodeUnsafe<T> next; 

    NodeUnsafe(T data) { 
     this.data = data; 
    } 

    NodeUnsafe<T> getNext() { 
     return next; 
    } 

    void setNext(NodeUnsafe<T> next) { 
     this.next = next; 
    } 
} 

Sau đó, tôi thực hiện hai phương pháp tương tự với sự khác biệt duy nhất - đầu tiên sử dụng Node<T> và thứ hai sử dụng NodeUsafe<T> lớp DeleteMiddle {

private static <T> T getLinkedList(int size, Function<Integer, T> supplier, BiConsumer<T, T> 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<Integer> head){ 
     Optional<Node<Integer>> oneStep = Optional.of(head); 
     Optional<Node<Integer>> doubleStep = oneStep; 
     Optional<Node<Integer>> prevStep = Optional.empty(); 

     while (doubleStep.isPresent() && doubleStep.get().getNext().isPresent()){ 
      doubleStep = doubleStep.get().getNext().get().getNext(); 
      prevStep = oneStep; 
      oneStep = oneStep.get().getNext(); 
     } 

     final Optional<Node<Integer>> toDelete = oneStep; 
     prevStep.ifPresent(s->s.setNext(toDelete.flatMap(Node::getNext))); 
    } 

    private static void deleteMiddleUnsafe(NodeUnsafe<Integer> head){ 
     NodeUnsafe<Integer> oneStep = head; 
     NodeUnsafe<Integer> doubleStep = oneStep; 
     NodeUnsafe<Integer> 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<Integer> head = getLinkedList(size, Node::new, Node::setNext); 
     Long before = System.currentTimeMillis(); 
     deleteMiddle(head); 
     System.out.println("Safe: " +(System.currentTimeMillis() - before)); 

     NodeUnsafe<Integer> headUnsafe = getLinkedList(size, NodeUnsafe::new, NodeUnsafe::setNext); 
     before = System.currentTimeMillis(); 
     deleteMiddleUnsafe(headUnsafe); 
     System.out.println("Unsafe: " +(System.currentTimeMillis() - before)); 
    } 
} 

So sánh hai lần chạy với kích thước khác nhau của danh sách cho thấy cách tiếp cận với mã số sử dụng Optional ở mức tốt nhất là hai lần chậm hơn một với nullables. Với các danh sách nhỏ, nó chậm hơn 3 lần.