2013-03-01 34 views
5

Tôi nghĩ rằng tôi đã hiểu sự khác biệt giữa ArrayList và LinkedList về mặt lý thuyết khá tốt. Tuy nhiên, đây là lần đầu tiên tôi đưa nó vào một bài kiểm tra nhỏ, và các bài kiểm tra xuất hiện, khác với những kỳ vọng của tôi.Sự khác biệt giữa ArrayList và LinkedList trong Java - whys cho hiệu suất

vọng:

  1. ArrayList sẽ chậm hơn so LinkedList khi chèn vào đầu, vì nó có để "thay đổi" các yếu tố, cho linkedlist, chỉ cập nhật tài liệu tham khảo 2 của nó.

    Thực tế: xuất hiện giống nhau trên hầu hết các lần lặp lại. Đối với một số lựa chọn số lặp lại, nó sẽ chậm hơn.

  2. Danh sách sẽ chậm hơn LinkedList khi xóa lúc đầu, vì nó phải "dịch chuyển" các phần tử, cho Danh sách liên kết, nó chỉ hủy một phần tử.

    Thực tế: Hiệu suất giống nhau khi xóa khỏi cầu xin.

Test Case: 1.000.000 yếu tố

public static void main(String[] args) { 
    int n = 1000000; 

    List arrayList = new ArrayList(n+10); 
    long milis = System.currentTimeMillis(); 
    for(int i= 0 ;i<n;i++){ 
     arrayList.add(i); 
    } 
    System.out.println("insert arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    List linkedList = new LinkedList(); 
    milis = System.currentTimeMillis(); 
    for(int i= 0 ;i<n;i++){ 
     linkedList.add(i); 
    } 
    System.out.println("insert linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    //System.out.println("Adding at end"); 
    milis = System.currentTimeMillis(); 
    arrayList.add(n-5,n+1); 
    System.out.println("APPEND arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.add(n-5,n+1); 
    System.out.println("APPEND linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    //add at front 
    milis = System.currentTimeMillis(); 
    arrayList.add(0,0); 
    System.out.println("INSERT BEG arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.add(0,0); 
    System.out.println("INSERT BEG linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    //add at middle 
    milis = System.currentTimeMillis(); 
    arrayList.add(n/2,n/2); 
    System.out.println("INSERT MIDDLE arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.add(n/2,n/2); 
    System.out.println("INSERT MIDDLE linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    //get from front, mid, end 
    milis = System.currentTimeMillis(); 
    arrayList.get(0); 
    System.out.println("get front arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.get(0); 
    System.out.println("get front linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    arrayList.get(n/2); 
    System.out.println("get MIDDLE arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.get(n/2); 
    System.out.println("get MIDDLE linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    arrayList.get(n-4); 
    System.out.println("get last arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.get(n-4); 
    System.out.println("get last linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    //delete from front, mid, end. 
    milis = System.currentTimeMillis(); 
    arrayList.remove(0); 
    System.out.println("del front arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.remove(0); 
    System.out.println("del front linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    arrayList.remove(n/2); 
    System.out.println("del mid arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.remove(n/2); 
    System.out.println("del mid linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    arrayList.remove(n-4); 
    System.out.println("del end arraylist takes "+(System.currentTimeMillis()-milis)+" ms"); 

    milis = System.currentTimeMillis(); 
    linkedList.remove(n-4); 
    System.out.println("del end linkedlist takes "+(System.currentTimeMillis()-milis)+" ms"); 

} 

Output Log

insert arraylist takes 141 ms 
insert linkedlist takes 312 ms 
APPEND arraylist takes 0 ms 
APPEND linkedlist takes 0 ms 
INSERT BEG arraylist takes 0 ms 
INSERT BEG linkedlist takes 0 ms 
INSERT MIDDLE arraylist takes 0 ms 
INSERT MIDDLE linkedlist takes 0 ms 
get front arraylist takes 0 ms 
get front linkedlist takes 0 ms 
get MIDDLE arraylist takes 0 ms 
get MIDDLE linkedlist takes 16 ms 
get last arraylist takes 0 ms 
get last linkedlist takes 0 ms 
del front arraylist takes 0 ms 
del front linkedlist takes 0 ms 
del mid arraylist takes 0 ms 
del mid linkedlist takes 15 ms 
del end arraylist takes 0 ms 
del end linkedlist takes 0 ms 

Vì vậy, lý do là gì? JDK 1.6 được sử dụng.

EDIT: Sau khi sử dụng System.nanotime(), nó đã cho tôi câu trả lời như tôi mong đợi. Đồng ý, chỉ một thử nghiệm duy nhất của nó và phải được tính trung bình.

insert arraylist takes 137076082 ns 
insert linkdlist takes 318985917 ns 
APPEND arraylist takes 69751 ns 
APPEND linkdlist takes 98126 ns 
**INSERT BEG arraylist takes 2027764 ns 
INSERT BEG linkdlist takes 53522 ns** 
INSERT MIDDLE arraylist takes 1008253 ns 
INSERT MIDDLE linkdlist takes 10395846 ns 
get front arraylist takes 42364 ns 
get front linkdlist takes 77473 ns 
get MIDDLE arraylist takes 39499 ns 
get MIDDLE linkdlist takes 9645996 ns 
get last arraylist takes 46165 ns 
get last linkdlist takes 43446 ns 
**del front arraylist takes 1720329 ns 
del front linkdlist takes 108063 ns** 
del mid arraylist takes 1157398 ns 
del mid linkdlist takes 11845077 ns 
del end arraylist takes 54149 ns 
del end linkdlist takes 49744 ns 
+4

khi thực hiện đo điểm chuẩn micro, luôn luôn sử dụng 'System.nanoTime()' – Nishant

+1

Tôi tự hỏi bao nhiêu thời gian autoboxing int mất trong trường hợp của bạn, trên pass đầu tiên của bạn, bạn sẽ được điền Integers cache –

+0

Đồng ý với @Nishant ở trên. 0ms cho hầu hết các thử nghiệm không hữu ích. Thời gian thực có thể là 50nanoseconds và 300nanoseconds. – mtariq

Trả lời

6

Lời giải thích cho số hai (lạ) thử nghiệm đầu tiên của bạn là:

Chèn vào ArrayList nói chung là chậm hơn bởi vì nó có phát triển một khi bạn nhấn ranh giới của nó. Nó sẽ phải tạo một mảng lớn hơn mới và sao chép dữ liệu từ mảng gốc.

Nhưng khi bạn tạo một ArrayList đó là đã lớn đủ để phù hợp với tất cả chèn của bạn (đó là trường hợp của bạn kể từ khi bạn đang làm new ArrayList(n+10)) - nó sẽ rõ ràng là không liên quan đến bất kỳ hoạt động mảng sao chép. Thêm vào nó sẽ thậm chí còn nhanh hơn với LinkedList vì LinkedList sẽ phải đối phó với "các liên kết" của nó (con trỏ), trong khi ArrayList lớn chỉ đặt giá trị tại chỉ mục (cuối).Ngoài ra các bài kiểm tra của bạn không tốt vì mỗi lần lặp lại liên quan đến autoboxing (chuyển đổi từ int thành Integer) - cả hai sẽ mất thêm thời gian để làm điều đó và cũng sẽ tăng kết quả vì số Integers cache sẽ được điền vào lần đầu tiên vượt qua.

2

int n = 1000000; quá nhỏ. Bạn nhận được 0 ms không thực sự có nghĩa là nó không mất thời gian để hoàn thành việc chèn hoặc xóa. Nó có nghĩa là thời gian trôi qua nhỏ hơn 1ms. Hãy thử tăng số lượng int n = 1000000;. Bạn có thể thấy sự khác biệt sau đó.

EDIT: Tôi đã đọc sai mã của bạn. Tôi nghĩ rằng bạn đang chèn các phần tử n vào trước danh sách mảng. Bạn chắc chắn nên chèn nhiều mục thay vì chỉ chèn một mục.

CHỈNH SỬA KHÁC: Nếu bạn chèn n yếu tố, bạn sẽ không cần phải tăng giá trị của n. Ngược lại, bạn có thể muốn giảm nó bởi vì chèn vào phía trước của ArrayList là một hoạt động chậm.

+0

@KarthikT Oh yes.Tôi nhận ra rằng ngay trước khi bạn đăng bình luận. Dù sao cũng cảm ơn bạn. –

0

Với một ArrayList, chèn vào hoạt động giữa sẽ là hai bước về lý thuyết Big Oh:

  • O (1) để tìm vị trí và chèn giá trị mới
  • O (n) để chuyển các giá trị còn lại về phía trước.

Với một LinkedList, nếu Node có liên quan hiện chưa được giải (looping từ nút đầu), nó cũng là hai bước sau:

  • O (n) để tìm Node ở vị trí
  • O (1) để chèn giá trị mới

Tuy nhiên, bên cạnh sự phức tạp thuật toán dự đoán, có những điểm khác có thể có ảnh hưởng đến thời gian chạy thực tế:

  • Triển khai ngôn ngữ cụ thể có thể làm cho các phần của quy trình cụ thể nhanh hơn. Trong trường hợp này, arraycopy của Java(), mà ArrayList sử dụng, các đường nối nhanh hơn một bản sao lặp cho mỗi giá trị. Điều này có thể không đúng đối với tất cả các mảng (kích thước, loại) - một lần nữa, nó sẽ được thực hiện cụ thể.

  • Big Oh bỏ qua các hằng số có thể tác động đến một số tập dữ liệu nhất định. Ví dụ, Bubble Sort là O (n * n) nhưng có thể phát hiện một mảng được sắp xếp trước trong O (n) và rất nhanh với các mảng được sắp xếp gần như sắp xếp.

  • LinkedList yêu cầu nhiều bộ nhớ hơn để được yêu cầu cho máy ảo (tạo đối tượng Nút). Các quy trình đòi hỏi nhiều bộ nhớ hơn để được yêu cầu đôi khi có thể là nguyên nhân gây tắc nghẽn.

Để tóm tắt: hãy cẩn thận với các giả định lý thuyết và luôn đo lường, tốt nhất là với dữ liệu và hoạt động thực tế :).

Các vấn đề liên quan