Mảng hoặc Danh sách trong Java. Cái nào nhanh hơn?


351

Tôi phải giữ hàng ngàn chuỗi trong bộ nhớ để được truy cập ser seri trong Java. Tôi nên lưu trữ chúng trong một mảng hay tôi nên sử dụng một số loại Danh sách?

Vì các mảng giữ tất cả dữ liệu trong một đoạn bộ nhớ liền kề (không giống như Danh sách), nên việc sử dụng một mảng để lưu trữ hàng ngàn chuỗi có gây ra sự cố không?


5
"Vì các mảng giữ tất cả dữ liệu trong một đoạn bộ nhớ liền kề", bạn có bất kỳ loại trích dẫn nào để sao lưu này cho Java không?
matt b

1
Không mờ. Tôi biết điều này cho C. Tôi đoán Java sẽ sử dụng cùng một phương thức.
euphoria83

Tôi nghi ngờ rằng nó sẽ giữ chúng trong một đoạn ký ức.
Fortyrunner

3
Ngay cả khi đó là một khối bộ nhớ duy nhất, nó vẫn chỉ có giá trị khoảng 1000 * 4 = 4kb, không phải là nhiều bộ nhớ.
CookieOfFortune

3
@mattb Đó là 'mảng' có nghĩa là gì trong suốt CS. Không cần trích dẫn. Nhiều tham chiếu trong JLS và [JVM Spec] () cho độ dài mảng chỉ có thể hiểu được nếu các mảng liền kề nhau.
Hầu tước Lorne

Câu trả lời:


358

Tôi đề nghị bạn nên sử dụng một hồ sơ để kiểm tra cái nào nhanh hơn.

Ý kiến ​​cá nhân của tôi là bạn nên sử dụng Danh sách.

Tôi làm việc trên một cơ sở mã lớn và một nhóm các nhà phát triển trước đó đã sử dụng mảng ở khắp mọi nơi . Nó làm cho mã rất không linh hoạt. Sau khi thay đổi khối lượng lớn của nó thành Danh sách, chúng tôi nhận thấy không có sự khác biệt về tốc độ.


2
@Fortyrunner - Từ kinh nghiệm của bạn, có bất kỳ lựa chọn nào như vậy trong Java giữa trừu tượng hóa và các dạng dữ liệu thô tạo ra sự khác biệt đáng kể về hiệu suất không?
euphoria83

4
Một trong những vấn đề với đo lường hiệu năng là bạn liên tục phải kiểm tra lại các phiên bản Java mới. Tôi đang giải quyết một vấn đề tại thời điểm mà ai đó đã sử dụng một int trong suốt cho một khóa trong bản đồ (để tiết kiệm không gian / thời gian). Bây giờ chúng ta cần thay đổi tất cả các dòng thành một đối tượng mới - đau đớn của nó.
Fortyrunner

9
Vì vậy, bây giờ tôi cố gắng và tránh xa dữ liệu thô. Nó hiếm khi làm cho một sự khác biệt đáng chú ý. Hotspot là một phần tuyệt vời của công nghệ và bạn không bao giờ nên thử và đoán thứ hai. Chỉ cần cố gắng viết mã đơn giản, có thể duy trì và Hotspot sẽ làm phần còn lại.
Fortyrunner

4
Hãy nhớ rằng kết quả trình lược tả chỉ hợp lệ đối với nền tảng Java mà bạn đang chạy trình lược tả. Đó có thể là một khác biệt so với khách hàng của bạn.
Mikkel Løkke

4
Java hiệu quả khuyến nghị Danh sách cho họ giúp khả năng tương tác API và cũng an toàn hơn với loại an toàn.
juanmf

164

Cách Java là bạn nên xem xét sự trừu tượng hóa dữ liệu nào phù hợp nhất với nhu cầu của bạn. Hãy nhớ rằng trong Java, Danh sách là một bản tóm tắt, không phải là kiểu dữ liệu cụ thể. Bạn nên khai báo các chuỗi dưới dạng Danh sách, sau đó khởi tạo nó bằng cách sử dụng triển khai ArrayList.

List<String> strings = new ArrayList<String>();

Sự tách biệt giữa Kiểu dữ liệu trừu tượng và việc triển khai cụ thể này là một trong những khía cạnh chính của lập trình hướng đối tượng.

Một ArrayList thực hiện Kiểu dữ liệu trừu tượng danh sách bằng cách sử dụng một mảng làm triển khai cơ bản của nó. Tốc độ truy cập gần như giống hệt với một mảng, với các ưu điểm bổ sung là có thể thêm và trừ các phần tử vào Danh sách (mặc dù đây là thao tác O (n) với ArrayList) và nếu bạn quyết định thay đổi triển khai cơ bản sau này bạn có thể. Ví dụ: nếu bạn nhận ra rằng bạn cần truy cập được đồng bộ hóa, bạn có thể thay đổi việc triển khai thành Vector mà không cần viết lại tất cả mã của mình.

Trong thực tế, ArrayList được thiết kế đặc biệt để thay thế cấu trúc mảng mức thấp trong hầu hết các bối cảnh. Nếu Java được thiết kế ngày hôm nay, thì hoàn toàn có khả năng các mảng sẽ bị loại bỏ hoàn toàn theo hướng có lợi cho cấu trúc ArrayList.

Vì các mảng giữ tất cả dữ liệu trong một đoạn bộ nhớ liền kề (không giống như Danh sách), nên việc sử dụng một mảng để lưu trữ hàng ngàn chuỗi có gây ra sự cố không?

Trong Java, tất cả các bộ sưu tập chỉ lưu trữ các tham chiếu đến các đối tượng, không phải chính các đối tượng. Cả mảng và ArrayList sẽ lưu trữ một vài nghìn tham chiếu trong một mảng liền kề, vì vậy chúng về cơ bản là giống hệt nhau. Bạn có thể xem xét rằng một khối liền kề gồm vài nghìn tham chiếu 32 bit sẽ luôn có sẵn trên phần cứng hiện đại. Điều này không đảm bảo rằng bạn sẽ không hết bộ nhớ, tất nhiên, chỉ là khối yêu cầu bộ nhớ liền kề không khó để hoàn thành.


Việc thêm tất nhiên có thể liên quan đến việc tái phân bổ mảng sao lưu, vì vậy nếu hiệu suất là quan trọng và kích thước của mảng được biết trước, thì ta nên xem xét sử dụng ArrayList # notifyCapacity.
JesperE

6
Bạn không trả chi phí ràng buộc năng động ở đây?
Uri

2
Tôi đoán việc thêm không phải là O (n) trong ArrayList, sẽ có một số hiệu ứng đạn dược khi thêm nhiều lần, ví dụ: dung lượng được tăng gấp đôi thay vì chỉ tăng 1.
zedoo

@enedoo Tôi nghĩ họ có nghĩa là cộng và trừ ở giữa.
MalcolmOcean

"Nếu Java được thiết kế ngày hôm nay, thì hoàn toàn có khả năng các mảng sẽ bị loại bỏ hoàn toàn theo hướng có lợi cho cấu trúc ArrayList." ... Tôi thực sự nghi ngờ rằng điều này sẽ là sự thật. Nếu đó là JVM được viết lại ngày hôm nay, thì những gì bạn đã nói chắc chắn là có thể. Nhưng với JVM mà chúng ta có, mảng là một kiểu nền tảng trong Java.
scottb

100

Mặc dù các câu trả lời đề xuất sử dụng ArrayList có ý nghĩa trong hầu hết các kịch bản, câu hỏi thực tế về hiệu suất tương đối chưa thực sự được trả lời.

Có một vài điều bạn có thể làm với một mảng:

  • tạo ra nó
  • đặt một mục
  • lấy một món đồ
  • sao chép / sao chép nó

Kết luận chung

Mặc dù các thao tác get và set có phần chậm hơn trên ArrayList (tương ứng 1 và 3 nano giây mỗi cuộc gọi trên máy của tôi), có rất ít chi phí sử dụng ArrayList so với mảng cho bất kỳ việc sử dụng không chuyên sâu nào. Tuy nhiên, có một số điều cần lưu ý:

  • Thay đổi kích thước các hoạt động trong danh sách (khi gọi list.add(...)) rất tốn kém và người ta nên cố gắng đặt công suất ban đầu ở mức phù hợp khi có thể (lưu ý rằng vấn đề tương tự phát sinh khi sử dụng một mảng)
  • khi giao dịch với người nguyên thủy, mảng có thể nhanh hơn đáng kể vì chúng sẽ cho phép người ta tránh được nhiều chuyển đổi đấm bốc / bỏ hộp
  • một ứng dụng chỉ nhận / đặt các giá trị trong một ArrayList (không phổ biến lắm!) có thể thấy mức tăng hiệu suất hơn 25% bằng cách chuyển sang một mảng

Kết quả chi tiết

Dưới đây là kết quả tôi đo được cho ba thao tác đó bằng thư viện điểm chuẩn jmh (thời gian tính bằng nano giây) với JDK 7 trên máy tính để bàn x86 tiêu chuẩn. Lưu ý rằng ArrayList không bao giờ được thay đổi kích thước trong các thử nghiệm để đảm bảo kết quả có thể so sánh được. Mã điểm chuẩn có sẵn ở đây .

Tạo mảng / ArrayList

Tôi đã chạy 4 bài kiểm tra, thực hiện các tuyên bố sau:

  • tạoArray1: Integer[] array = new Integer[1];
  • tạoList1: List<Integer> list = new ArrayList<> (1);
  • tạoArray10000: Integer[] array = new Integer[10000];
  • tạoList10000: List<Integer> list = new ArrayList<> (10000);

Kết quả (tính bằng nano giây trên mỗi cuộc gọi, độ tin cậy 95%):

a.p.g.a.ArrayVsList.CreateArray1         [10.933, 11.097]
a.p.g.a.ArrayVsList.CreateList1          [10.799, 11.046]
a.p.g.a.ArrayVsList.CreateArray10000    [394.899, 404.034]
a.p.g.a.ArrayVsList.CreateList10000     [396.706, 401.266]

Kết luận: không có sự khác biệt đáng chú ý .

có được hoạt động

Tôi đã chạy 2 bài kiểm tra, thực hiện các tuyên bố sau:

  • danh sách: return list.get(0);
  • getArray: return array[0];

Kết quả (tính bằng nano giây trên mỗi cuộc gọi, độ tin cậy 95%):

a.p.g.a.ArrayVsList.getArray   [2.958, 2.984]
a.p.g.a.ArrayVsList.getList    [3.841, 3.874]

Kết luận: nhận được từ một mảng nhanh hơn khoảng 25% so với nhận được từ một ArrayList, mặc dù sự khác biệt chỉ theo thứ tự một nano giây.

thiết lập hoạt động

Tôi đã chạy 2 bài kiểm tra, thực hiện các tuyên bố sau:

  • lập danh sách: list.set(0, value);
  • setArray: array[0] = value;

Kết quả (tính bằng nano giây trên mỗi cuộc gọi):

a.p.g.a.ArrayVsList.setArray   [4.201, 4.236]
a.p.g.a.ArrayVsList.setList    [6.783, 6.877]

Kết luận: các thao tác thiết lập trên mảng nhanh hơn khoảng 40% so với danh sách, nhưng đối với get, mỗi thao tác thiết lập mất vài nano giây - do đó, để chênh lệch đạt 1 giây, người ta sẽ cần đặt các mục trong danh sách / mảng hàng trăm của hàng triệu lần!

bản sao / bản sao

Đại biểu copy constructor ArrayList để Arrays.copyOfdo đó hiệu suất giống hệt bản sao mảng (sao chép một mảng qua clone, Arrays.copyOfhoặc System.arrayCopy làm cho không có sự khác biệt liệu hiệu suất-khôn ngoan ).


1
Phân tích tốt đẹp. Tuy nhiên, liên quan đến nhận xét của bạn "khi xử lý các nguyên thủy, mảng có thể nhanh hơn đáng kể vì chúng sẽ cho phép một người tránh được nhiều chuyển đổi đấm bốc / bỏ hộp", bạn có thể có bánh của mình và ăn nó, với Danh sách được hỗ trợ bởi mảng nguyên thủy thực hiện; ví dụ: github.com/scijava/scijava-common/blob/master/src/main/java/org/ trộm . Tôi thực sự khá ngạc nhiên khi một thứ như vậy đã không biến nó thành lõi Java.
ctrueden

2
@ctrueden có nhận xét được áp dụng cho JDK ArrayList tiêu chuẩn. trove4j là một thư viện nổi tiếng hỗ trợ các danh sách nguyên thủy. Java 8 mang đến một số cải tiến với một số Luồng chuyên dụng nguyên thủy.
assylias

Tôi không biết làm thế nào các tiêu chuẩn jmh hoạt động nhưng họ có tính đến việc biên dịch JIT có thể xảy ra không? Hiệu năng của một ứng dụng Java có thể thay đổi theo thời gian khi JVM biên dịch mã của bạn.
Hoffmann

@Hoffmann Có - nó bao gồm một giai đoạn khởi động được loại trừ khỏi phép đo.
assylias

97

Bạn nên thích các loại chung hơn mảng. Như đã đề cập bởi những người khác, mảng là không linh hoạt và không có sức mạnh biểu cảm của các loại chung. (Tuy nhiên, chúng hỗ trợ đánh máy thời gian chạy, nhưng kết hợp không tốt với các kiểu chung.)

Nhưng, như mọi khi, khi tối ưu hóa, bạn nên luôn luôn làm theo các bước sau:

  • Đừng tối ưu hóa cho đến khi bạn có một phiên bản mã đẹp, gọn gàng và hoạt động tốt . Thay đổi thành các loại chung rất có thể được thúc đẩy ở bước này.
  • Khi bạn có một phiên bản đẹp và sạch sẽ, hãy quyết định xem nó có đủ nhanh không.
  • Nếu nó không đủ nhanh, hãy đo hiệu suất của nó . Bước này rất quan trọng vì hai lý do. Nếu bạn không đo lường, bạn sẽ không (1) biết tác động của bất kỳ tối ưu hóa nào bạn thực hiện và (2) biết nơi tối ưu hóa.
  • Tối ưu hóa phần nóng nhất của mã của bạn.
  • Đo lại. Điều này cũng quan trọng như đo lường trước đây. Nếu việc tối ưu hóa không cải thiện mọi thứ, hãy hoàn nguyên nó . Hãy nhớ rằng, mã mà không tối ưu hóa là sạch sẽ, tốt đẹp và làm việc.

24

Tôi đoán rằng poster ban đầu đến từ nền C ++ / STL đang gây ra một số nhầm lẫn. Trong C ++ std::listlà một danh sách liên kết đôi.

Trong Java [java.util.]Listlà một giao diện không có triển khai (lớp trừu tượng thuần túy theo thuật ngữ C ++). Listcó thể là một danh sách liên kết đôi - java.util.LinkedListđược cung cấp. Tuy nhiên, 99 lần trong số 100 khi bạn muốn tạo mới List, java.util.ArrayListthay vào đó bạn muốn sử dụng , tương đương với C ++ std::vector. Có những triển khai tiêu chuẩn khác, chẳng hạn như những triển khai được trả về java.util.Collections.emptyList()java.util.Arrays.asList().

Từ quan điểm hiệu suất, có một điểm nhấn rất nhỏ từ việc phải đi qua một giao diện và một đối tượng phụ, tuy nhiên thời gian chạy nội tuyến có nghĩa là điều này hiếm khi có bất kỳ ý nghĩa nào. Cũng nên nhớ rằng Stringthường là một đối tượng cộng với mảng. Vì vậy, đối với mỗi mục, bạn có thể có hai đối tượng khác. Trong C ++ std::vector<std::string>, mặc dù sao chép theo giá trị mà không có con trỏ như vậy, các mảng ký tự sẽ tạo thành một đối tượng cho chuỗi (và chúng thường không được chia sẻ).

Nếu mã đặc biệt này thực sự nhạy cảm về hiệu năng, bạn có thể tạo một char[]mảng duy nhất (hoặc thậm chí byte[]) cho tất cả các ký tự của tất cả các chuỗi và sau đó là một mảng bù. IIRC, đây là cách javac được thực hiện.


1
Thanx cho câu trả lời. Nhưng không, tôi không nhầm lẫn danh sách C ++ với Danh sách giao diện của Java. Tôi đã đặt câu hỏi theo cách như vậy bởi vì tôi muốn so sánh hiệu suất của việc triển khai Danh sách như ArrayList và Vector với mảng thô.
euphoria83

Cả ArrayList và Vector "giữ tất cả dữ liệu trong một đoạn bộ nhớ liền kề".
Tom Hawtin - tackline

13

Tôi đồng ý rằng trong hầu hết các trường hợp, bạn nên chọn tính linh hoạt và thanh lịch của ArrayLists trên các mảng - và trong hầu hết các trường hợp, tác động đến hiệu suất chương trình sẽ không đáng kể.

Tuy nhiên, nếu bạn đang thực hiện liên tục, lặp lại nặng nề với ít thay đổi cấu trúc (không thêm và xóa), ví dụ, kết xuất đồ họa phần mềm hoặc máy ảo tùy chỉnh, các kiểm tra điểm chuẩn truy cập tuần tự của tôi cho thấy ArrayLists chậm hơn 1,5 lần so với mảng trên tôi hệ thống (Java 1.6 trên iMac một năm tuổi của tôi).

Một số mã:

import java.util.*;

public class ArrayVsArrayList {
    static public void main( String[] args ) {

        String[] array = new String[300];
        ArrayList<String> list = new ArrayList<String>(300);

        for (int i=0; i<300; ++i) {
            if (Math.random() > 0.5) {
                array[i] = "abc";
            } else {
                array[i] = "xyz";
            }

            list.add( array[i] );
        }

        int iterations = 100000000;
        long start_ms;
        int sum;

        start_ms = System.currentTimeMillis();
        sum = 0;

        for (int i=0; i<iterations; ++i) {
          for (int j=0; j<300; ++j) sum += array[j].length();
        }

        System.out.println( (System.currentTimeMillis() - start_ms) + " ms (array)" );
        // Prints ~13,500 ms on my system

        start_ms = System.currentTimeMillis();
        sum = 0;

        for (int i=0; i<iterations; ++i) {
          for (int j=0; j<300; ++j) sum += list.get(j).length();
        }

        System.out.println( (System.currentTimeMillis() - start_ms) + " ms (ArrayList)" );
        // Prints ~20,800 ms on my system - about 1.5x slower than direct array access
    }
}

Tôi thấy đây là một câu trả lời thú vị, nhưng tôi tự hỏi liệu nó còn tệ hơn nữa nếu ArrayList không được khởi tạo với kích thước ban đầu trong bộ nhớ. Nói chung, lợi ích của việc sử dụng ArrayList trên một mảng nguyên gốc theo nghĩa là bạn sẽ không biết và bạn không phải lo lắng. ArrayLists theo mặc định được tạo với độ dài ban đầu 10 và sau đó được thay đổi kích thước. Tôi nghĩ rằng việc thay đổi kích thước là tốn kém. Tôi đã không thử điểm chuẩn rõ ràng.
Zak Patterson

4
Điểm chuẩn vi mô này có sai sót (không khởi động, hoạt động không theo một phương thức riêng biệt nên phần danh sách mảng không bao giờ được tối ưu hóa bởi JIT, v.v.)
assylias

Tôi đồng ý với assylias. Kết quả của điểm chuẩn này không đáng tin cậy.
Stephen C

@StephenC Tôi đã thêm một điểm chuẩn vi mô phù hợp (điều đó cho thấy các hoạt động có thể so sánh được).
assylias

11

Đầu tiên, nó đáng để làm rõ, bạn có nghĩa là "danh sách" theo nghĩa cấu trúc dữ liệu comp sci cổ điển (nghĩa là một danh sách được liên kết) hoặc bạn có nghĩa là java.util.List? Nếu bạn có nghĩa là java.util.List, thì đó là một giao diện. Nếu bạn muốn sử dụng một mảng, chỉ cần sử dụng triển khai ArrayList và bạn sẽ có hành vi và ngữ nghĩa giống như mảng. Vấn đề được giải quyết.

Nếu bạn có nghĩa là một mảng so với một danh sách được liên kết, thì đó là một đối số hơi khác mà chúng ta quay lại Big O (đây là một lời giải thích bằng tiếng Anh nếu đây là một thuật ngữ không quen thuộc.

Mảng;

  • Truy cập ngẫu nhiên: O (1);
  • Chèn: O (n);
  • Xóa: O (n).

Danh sách liên kết:

  • Truy cập ngẫu nhiên: O (n);
  • Chèn: O (1);
  • Xóa: O (1).

Vì vậy, bạn chọn bất cứ ai phù hợp nhất với cách bạn thay đổi kích thước mảng của bạn. Nếu bạn thay đổi kích thước, chèn và xóa nhiều thì có lẽ danh sách được liên kết là lựa chọn tốt hơn. Tương tự như vậy nếu truy cập ngẫu nhiên là hiếm. Bạn đề cập đến truy cập nối tiếp. Nếu bạn chủ yếu thực hiện truy cập nối tiếp với rất ít sửa đổi thì có lẽ bạn không chọn vấn đề gì.

Danh sách được liên kết có chi phí cao hơn một chút vì, như bạn nói, bạn đang xử lý các khối bộ nhớ không liền kề tiềm năng và (có hiệu quả) con trỏ đến phần tử tiếp theo. Đó có lẽ không phải là một yếu tố quan trọng trừ khi bạn xử lý hàng triệu mục.


ý tôi là giao diện java.util.List
euphoria83

1
Truy cập ngẫu nhiên O (n) trên danh sách liên kết có vẻ như là một vấn đề lớn đối với tôi.
Bjorn

11

Tôi đã viết một điểm chuẩn nhỏ để so sánh ArrayLists với Mảng. Trên máy tính xách tay cũ của tôi, thời gian để duyệt qua danh sách mảng 5000 phần tử, 1000 lần, chậm hơn khoảng 10 mili giây so với mã mảng tương đương.

Vì vậy, nếu bạn không làm gì ngoài việc lặp lại danh sách và bạn đang làm nó rất nhiều, thì có lẽ nó đáng để tối ưu hóa. Nếu không tôi muốn sử dụng danh sách, bởi vì nó sẽ làm cho nó dễ dàng hơn khi bạn làm cần thiết để tối ưu hóa mã.

nb Tôi đã nhận thấy rằng việc sử dụng for String s: stringsListchậm hơn khoảng 50% so với sử dụng vòng lặp for kiểu cũ để truy cập danh sách. Đi nào ... Đây là hai chức năng tôi đã hẹn giờ; mảng và danh sách được lấp đầy với 5000 chuỗi ngẫu nhiên (khác nhau).

private static void readArray(String[] strings) {
    long totalchars = 0;
    for (int j = 0; j < ITERATIONS; j++) {
        totalchars = 0;
        for (int i = 0; i < strings.length; i++) {
            totalchars += strings[i].length();

        }
    }
}

private static void readArrayList(List<String> stringsList) {
    long totalchars = 0;
    for (int j = 0; j < ITERATIONS; j++) {
        totalchars = 0;
        for (int i = 0; i < stringsList.size(); i++) {
            totalchars += stringsList.get(i).length();
        }
    }
}

@ Chris May: Công việc tuyệt vời! Thời gian chạy thực tế cho cả hai là gì? Bạn có thể cho tôi biết kích thước của chuỗi bạn đang sử dụng? Ngoài ra, do việc sử dụng 'String s: StringList' khiến nó mất nhiều thời gian hơn, đây là nỗi sợ chính của tôi khi sử dụng các khái niệm trừu tượng cao hơn trong Java nói chung.
euphoria83

Nó thực sự không quan trọng bao lâu các chuỗi cho mcirobenchmark này. Không có gc, và char[]không được chạm vào (đây không phải là C).
Tom Hawtin - tackline

Thời gian điển hình đối với tôi là ~ 25ms cho phiên bản mảng, ~ 35ms cho phiên bản ArrayList. Các chuỗi dài 15-20 ký tự. Như Tom nói, kích thước chuỗi không tạo ra nhiều sự khác biệt, với chuỗi ~ 100 ký tự thời gian là như nhau.
Chris May 5 tháng

3
Làm thế nào bạn đo được? Đo lường ngây thơ trong các điểm chuẩn vi mô Java thường tạo ra nhiều thông tin sai lệch hơn thông tin. Coi chừng tuyên bố trên.
jmg

6

Không, bởi vì về mặt kỹ thuật, mảng chỉ lưu trữ tham chiếu đến các chuỗi. Các chuỗi được phân bổ ở một vị trí khác nhau. Đối với một ngàn mục, tôi sẽ nói rằng một danh sách sẽ tốt hơn, chậm hơn, nhưng nó mang lại sự linh hoạt hơn và dễ sử dụng hơn, đặc biệt nếu bạn định thay đổi kích thước chúng.


5
Danh sách cũng lưu trữ chỉ tham chiếu đến chuỗi.
Peter tibraný

6

Nếu bạn có hàng ngàn, hãy xem xét sử dụng một trie. Trie là một cấu trúc giống như cây, hợp nhất các tiền tố phổ biến của chuỗi được lưu trữ.

Ví dụ: nếu các chuỗi là

intern
international
internationalize
internet
internets

Bộ ba sẽ lưu trữ:

intern
 -> \0
 international
 -> \0
 -> ize\0
 net
 ->\0
 ->s\0

Các chuỗi yêu cầu 57 ký tự (bao gồm cả bộ kết thúc null, '\ 0') để lưu trữ, cộng với bất kỳ kích thước nào của đối tượng Chuỗi chứa chúng. (Trong thực tế, có lẽ chúng ta nên làm tròn tất cả các kích thước lên tới bội số của 16, nhưng ...) Gọi nó là 57 + 5 = 62 byte, đại khái.

Bộ ba yêu cầu 29 (bao gồm cả bộ kết thúc null, '\ 0') để lưu trữ, cộng với kích thước của các nút trie, là một tham chiếu đến một mảng và một danh sách các nút trie con.

Đối với ví dụ này, điều đó có thể đi ra về cùng; đối với hàng ngàn, nó có thể xuất hiện ít hơn miễn là bạn có tiền tố chung.

Bây giờ, khi sử dụng bộ ba trong mã khác, bạn sẽ phải chuyển đổi thành Chuỗi, có thể sử dụng StringBuffer làm trung gian. Nếu nhiều chuỗi được sử dụng cùng một lúc như Chuỗi, ngoài bộ ba, đó là một mất mát.

Nhưng nếu bạn chỉ sử dụng một số ít vào thời điểm đó - giả sử, để tra cứu mọi thứ trong từ điển - bộ ba có thể giúp bạn tiết kiệm rất nhiều không gian. Chắc chắn ít không gian hơn so với việc lưu trữ chúng trong Hashset.

Bạn nói rằng bạn đang truy cập chúng một cách "thanh thản" - nếu điều đó có nghĩa là tuần tự theo thứ tự bảng chữ cái, thì bộ ba rõ ràng cũng cung cấp cho bạn thứ tự bảng chữ cái miễn phí, nếu bạn lặp lại theo chiều sâu.


1
Trie giống như một thư viện hay làm thế nào để tôi tạo ra nó?
euphoria83

Một trie sẽ chỉ hữu ích trong trường hợp các chuỗi được mã hóa, chứ không phải nếu ai đó đang lưu trữ văn bản đang chạy dưới dạng chuỗi.
MN

5

CẬP NHẬT:

Như Mark đã lưu ý, không có sự khác biệt đáng kể nào sau khi JVM khởi động (một vài lần thử nghiệm). Đã kiểm tra với mảng được tạo lại hoặc thậm chí vượt qua mới bắt đầu với hàng ma trận mới. Với khả năng lớn, dấu hiệu này là mảng đơn giản với quyền truy cập chỉ mục sẽ không được sử dụng để ủng hộ các bộ sưu tập.

Vẫn 1-2 lần đầu tiên vượt qua mảng đơn giản là nhanh hơn 2-3 lần.

BÀI VIẾT GỐC:

Quá nhiều từ cho chủ đề quá đơn giản để kiểm tra. Không có bất kỳ mảng câu hỏi nào nhanh hơn nhiều lần so với bất kỳ lớp chứa nào . Tôi chạy theo câu hỏi này để tìm giải pháp thay thế cho phần quan trọng về hiệu suất của tôi. Đây là mã nguyên mẫu tôi xây dựng để kiểm tra tình hình thực tế:

import java.util.List;
import java.util.Arrays;

public class IterationTest {

    private static final long MAX_ITERATIONS = 1000000000;

    public static void main(String [] args) {

        Integer [] array = {1, 5, 3, 5};
        List<Integer> list = Arrays.asList(array);

        long start = System.currentTimeMillis();
        int test_sum = 0;
        for (int i = 0; i < MAX_ITERATIONS; ++i) {
//            for (int e : array) {
            for (int e : list) {
                test_sum += e;
            }
        }
        long stop = System.currentTimeMillis();

        long ms = (stop - start);
        System.out.println("Time: " + ms);
    }
}

Và đây là câu trả lời:

Dựa trên mảng (dòng 16 đang hoạt động):

Time: 7064

Dựa trên danh sách (dòng 17 đang hoạt động):

Time: 20950

Có nhận xét nào thêm về 'nhanh hơn' không? Điều này khá dễ hiểu. Câu hỏi là khi nào nhanh hơn khoảng 3 lần thì tốt hơn cho bạn so với tính linh hoạt của Danh sách. Nhưng đây là một câu hỏi khác. Bằng cách này, tôi đã kiểm tra điều này quá dựa trên xây dựng thủ công ArrayList. Gần như cùng một kết quả.


2
3lần nhanh hơn đúng, nhưng không đáng kể như vậy. 14mskhông phải là một thời gian dài
0x6C38

1
Điểm chuẩn không xem xét khởi động JVM. Thay đổi main () thành test () và gọi kiểm tra từ main nhiều lần. Đến lần chạy thử thứ 3 hoặc 4, nó chạy nhanh hơn nhiều lần. Vào thời điểm đó, tôi thấy mảng đó nhanh hơn mảng khoảng 9 lần.
Mike

5

Vì đã có rất nhiều câu trả lời hay ở đây, tôi muốn cung cấp cho bạn một số thông tin khác về quan điểm thực tế, đó là so sánh hiệu năng chèn và lặp: mảng nguyên thủy so với danh sách liên kết trong Java.

Đây là kiểm tra hiệu suất thực tế đơn giản.
Vì vậy, kết quả sẽ phụ thuộc vào hiệu suất máy.

Mã nguồn được sử dụng cho điều này là dưới đây:

import java.util.Iterator;
import java.util.LinkedList;

public class Array_vs_LinkedList {

    private final static int MAX_SIZE = 40000000;

    public static void main(String[] args) {

        LinkedList lList = new LinkedList(); 

        /* insertion performance check */

        long startTime = System.currentTimeMillis();

        for (int i=0; i<MAX_SIZE; i++) {
            lList.add(i);
        }

        long stopTime = System.currentTimeMillis();
        long elapsedTime = stopTime - startTime;
        System.out.println("[Insert]LinkedList insert operation with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");

        int[] arr = new int[MAX_SIZE];

        startTime = System.currentTimeMillis();
        for(int i=0; i<MAX_SIZE; i++){
            arr[i] = i; 
        }

        stopTime = System.currentTimeMillis();
        elapsedTime = stopTime - startTime;
        System.out.println("[Insert]Array Insert operation with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");


        /* iteration performance check */

        startTime = System.currentTimeMillis();

        Iterator itr = lList.iterator();

        while(itr.hasNext()) {
            itr.next();
            // System.out.println("Linked list running : " + itr.next());
        }

        stopTime = System.currentTimeMillis();
        elapsedTime = stopTime - startTime;
        System.out.println("[Loop]LinkedList iteration with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");


        startTime = System.currentTimeMillis();

        int t = 0;
        for (int i=0; i < MAX_SIZE; i++) {
            t = arr[i];
            // System.out.println("array running : " + i);
        }

        stopTime = System.currentTimeMillis();
        elapsedTime = stopTime - startTime;
        System.out.println("[Loop]Array iteration with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");
    }
}

Kết quả thực hiện dưới đây:

nhập mô tả hình ảnh ở đây


4

danh sách chậm hơn mảng. Nếu bạn cần hiệu quả sử dụng mảng. Nếu bạn cần danh sách sử dụng linh hoạt.


4

Hãy nhớ rằng một ArrayList đóng gói một mảng, do đó có rất ít sự khác biệt so với việc sử dụng một mảng nguyên thủy (ngoại trừ thực tế là một Danh sách dễ làm việc hơn trong java).

Khá nhiều lần duy nhất có ý nghĩa để thích một mảng hơn với ArrayList là khi bạn đang lưu trữ các nguyên hàm, tức là byte, int, v.v. và bạn cần hiệu quả không gian cụ thể mà bạn có được bằng cách sử dụng các mảng nguyên thủy.


4

Lựa chọn mảng so với danh sách không quá quan trọng (xem xét hiệu năng) trong trường hợp lưu trữ các đối tượng chuỗi. Bởi vì cả mảng và danh sách sẽ lưu trữ các tham chiếu đối tượng chuỗi, không phải các đối tượng thực tế.

  1. Nếu số lượng chuỗi gần như không đổi thì hãy sử dụng một mảng (hoặc ArrayList). Nhưng nếu số lượng thay đổi quá nhiều thì tốt hơn bạn nên sử dụng LinkedList.
  2. Nếu có (hoặc sẽ) cần thêm hoặc xóa các phần tử ở giữa, thì bạn chắc chắn phải sử dụng LinkedList.

4

Tôi đến đây để có được cảm giác tốt hơn về tác động hiệu suất của việc sử dụng danh sách trên các mảng. Tôi đã phải điều chỉnh mã ở đây cho kịch bản của mình: mảng / danh sách ~ 1000 ints sử dụng chủ yếu là getters, nghĩa là mảng [j] so với list.get (j)

Lấy thứ tốt nhất trong số 7 là không khoa học về nó (vài cái đầu tiên với danh sách chậm hơn 2,5 lần) tôi nhận được điều này:

array Integer[] best 643ms iterator
ArrayList<Integer> best 1014ms iterator

array Integer[] best 635ms getter
ArrayList<Integer> best 891ms getter (strange though)

- vì vậy, nhanh hơn khoảng 30% với mảng

Lý do thứ hai để đăng bây giờ là không ai đề cập đến tác động nếu bạn thực hiện mã toán học / ma trận / mô phỏng / tối ưu hóa với lồng nhau vòng lặp .

Giả sử bạn có ba cấp độ lồng nhau và vòng lặp bên trong chậm gấp đôi so với khi bạn nhìn vào hiệu suất 8 lần. Một cái gì đó sẽ chạy trong một ngày bây giờ mất một tuần.

* EDIT Khá sốc ở đây, vì những cú đá tôi đã thử khai báo int [1000] thay vì Integer [1000]

array int[] best 299ms iterator
array int[] best 296ms getter

Sử dụng Integer [] so với int [] thể hiện lần truy cập hiệu năng kép, ListArray với iterator chậm hơn 3x so với int []. Thực sự nghĩ rằng việc triển khai danh sách của Java tương tự như mảng gốc ...

Mã để tham khảo (gọi nhiều lần):

    public static void testArray()
    {
        final long MAX_ITERATIONS = 1000000;
        final int MAX_LENGTH = 1000;

        Random r = new Random();

        //Integer[] array = new Integer[MAX_LENGTH];
        int[] array = new int[MAX_LENGTH];

        List<Integer> list = new ArrayList<Integer>()
        {{
            for (int i = 0; i < MAX_LENGTH; ++i)
            {
                int val = r.nextInt();
                add(val);
                array[i] = val;
            }
        }};

        long start = System.currentTimeMillis();
        int test_sum = 0;
        for (int i = 0; i < MAX_ITERATIONS; ++i)
        {
//          for (int e : array)
//          for (int e : list)          
            for (int j = 0; j < MAX_LENGTH; ++j)
            {
                int e = array[j];
//              int e = list.get(j);
                test_sum += e;
            }
        }

        long stop = System.currentTimeMillis();

        long ms = (stop - start);
        System.out.println("Time: " + ms);
    }

3

Nếu bạn biết trước dữ liệu lớn như thế nào thì một mảng sẽ nhanh hơn.

Một danh sách linh hoạt hơn. Bạn có thể sử dụng một ArrayList được hỗ trợ bởi một mảng.


ArrayList có một phương thức notifyCapacity () để sắp xếp mảng sao lưu theo kích thước đã chỉ định.
JesperE

Hoặc bạn có thể chỉ định kích thước tại thời điểm xây dựng. Ngoài ra "nhanh hơn" ở đây có nghĩa là "một vài micro giây để phân bổ hai vùng nhớ thay vì một"
Aaron Digulla

3

Bạn có thể sống với một kích thước cố định, các mảng sẽ nhanh hơn và cần ít bộ nhớ hơn.

Nếu bạn cần sự linh hoạt của giao diện Danh sách với việc thêm và xóa các thành phần, câu hỏi vẫn là bạn nên chọn triển khai nào. Thường thì ArrayList được khuyến nghị và sử dụng cho mọi trường hợp, nhưng ArrayList cũng có vấn đề về hiệu năng nếu các phần tử ở đầu hoặc ở giữa danh sách phải được gỡ bỏ hoặc chèn vào.

Do đó, bạn có thể muốn xem http://java.dzone.com/articles/gaplist-%E2%80%93-lightning-fast-list giới thiệu GapList. Việc triển khai danh sách mới này kết hợp các điểm mạnh của cả ArrayList và LinkedList dẫn đến hiệu suất rất tốt cho gần như tất cả các hoạt động.


2

Tùy theo việc thực hiện. có thể một mảng các kiểu nguyên thủy sẽ nhỏ hơn và hiệu quả hơn ArrayList. Điều này là do mảng sẽ lưu trữ các giá trị trực tiếp trong một khối bộ nhớ liền kề, trong khi việc triển khai ArrayList đơn giản nhất sẽ lưu trữ các con trỏ tới mỗi giá trị. Trên nền tảng 64 bit đặc biệt, điều này có thể tạo ra sự khác biệt rất lớn.

Tất nhiên, việc triển khai jvm có thể có trường hợp đặc biệt cho tình huống này, trong trường hợp đó hiệu suất sẽ giống nhau.


2

Danh sách là cách ưa thích trong java 1.5 và hơn thế nữa vì nó có thể sử dụng generic. Mảng không thể có thuốc generic. Ngoài ra Mảng có độ dài được xác định trước, không thể phát triển linh hoạt. Khởi tạo một mảng với kích thước lớn không phải là một ý tưởng tốt. ArrayList là cách để khai báo một mảng với generic và nó có thể tự động phát triển. Nhưng nếu xóa và chèn được sử dụng thường xuyên hơn, thì danh sách liên kết là cấu trúc dữ liệu nhanh nhất được sử dụng.


2

Mảng được đề xuất ở mọi nơi bạn có thể sử dụng chúng thay vì danh sách, đặc biệt là trong trường hợp nếu bạn biết số lượng và kích thước vật phẩm sẽ không thay đổi.

Xem thực hành tốt nhất về Java Java: http://docs.oracle.com/cd/A97688_16/generic.903/bp/java.htmlm#1007056

Tất nhiên, nếu bạn cần thêm và xóa các đối tượng khỏi bộ sưu tập nhiều lần danh sách sử dụng dễ dàng.


Tài liệu bạn liên kết đến hơn 10 năm tuổi, tức là áp dụng cho java 1.3. Những cải tiến hiệu suất lớn đã được thực hiện kể từ đó ...
assylias

@assylias xem câu trả lời ở trên, chúng chứa các bài kiểm tra hiệu suất, nói rằng các mảng nhanh hơn
Nik

3
Tôi biết tôi đã viết một trong số họ. Nhưng tôi không nghĩ rằng " mảng được khuyến nghị ở mọi nơi bạn có thể sử dụng chúng thay vì danh sách " là một lời khuyên tốt. ArrayList nên là lựa chọn mặc định trong hầu hết các tình huống trừ khi bạn đang xử lý các nguyên hàm và mã của bạn nhạy cảm với hiệu năng.
assylias

2

Không có câu trả lời nào có thông tin mà tôi quan tâm - quét lặp đi lặp lại cùng một mảng nhiều lần. Phải tạo ra một bài kiểm tra JMH cho việc này.

Kết quả (Java 1.8.0_66 x32, lặp lại mảng đơn giản nhanh hơn ít nhất 5 lần so với ArrayList):

Benchmark                    Mode  Cnt   Score   Error  Units
MyBenchmark.testArrayForGet  avgt   10   8.121 ? 0.233  ms/op
MyBenchmark.testListForGet   avgt   10  37.416 ? 0.094  ms/op
MyBenchmark.testListForEach  avgt   10  75.674 ? 1.897  ms/op

Kiểm tra

package my.jmh.test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

@State(Scope.Benchmark)
@Fork(1)
@Warmup(iterations = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class MyBenchmark {

    public final static int ARR_SIZE = 100;
    public final static int ITER_COUNT = 100000;

    String arr[] = new String[ARR_SIZE];
    List<String> list = new ArrayList<>(ARR_SIZE);

    public MyBenchmark() {
        for( int i = 0; i < ARR_SIZE; i++ ) {
            list.add(null);
        }
    }

    @Benchmark
    public void testListForEach() {
        int count = 0;
        for( int i = 0; i < ITER_COUNT; i++ ) {
            for( String str : list ) {
                if( str != null )
                    count++;
            }
        }
        if( count > 0 )
            System.out.print(count);
    }

    @Benchmark
    public void testListForGet() {
        int count = 0;
        for( int i = 0; i < ITER_COUNT; i++ ) {
            for( int j = 0; j < ARR_SIZE; j++ ) {
                if( list.get(j) != null )
                    count++;
            }
        }
        if( count > 0 )
            System.out.print(count);
    }

    @Benchmark
    public void testArrayForGet() {
        int count = 0;
        for( int i = 0; i < ITER_COUNT; i++ ) {
            for( int j = 0; j < ARR_SIZE; j++ ) {
                if( arr[j] != null )
                    count++;
            }
        }
        if( count > 0 )
            System.out.print(count);
    }

}

2

"Hàng ngàn" không phải là một số lượng lớn. Một vài nghìn chuỗi có độ dài đoạn được sắp xếp theo thứ tự của một vài megabyte. Nếu tất cả những gì bạn muốn làm là truy cập vào những trang này, hãy sử dụng Danh sách liên kết đơn bất biến .


8 byte trên hầu hết các triển khai 64 bit.
Tom Hawtin - tackline

Có bằng chứng nào cho thấy điều này nhanh hơn java.util.LinkedList không? Đó cũng là 'trong bộ nhớ'? Nó cũng có thể được làm cho bất biến, như thể điều đó làm cho bất kỳ sự khác biệt.
Hầu tước Lorne

1

Đừng rơi vào cái bẫy tối ưu hóa mà không có điểm chuẩn thích hợp. Như những người khác đã đề nghị sử dụng một hồ sơ trước khi đưa ra bất kỳ giả định.

Các cấu trúc dữ liệu khác nhau mà bạn đã liệt kê có các mục đích khác nhau. Một danh sách rất hiệu quả trong việc chèn các phần tử vào đầu và cuối nhưng chịu rất nhiều khi truy cập các phần tử ngẫu nhiên. Một mảng có lưu trữ cố định nhưng cung cấp truy cập ngẫu nhiên nhanh chóng. Cuối cùng, một ArrayList cải thiện giao diện thành một mảng bằng cách cho phép nó phát triển. Thông thường, cấu trúc dữ liệu được sử dụng nên được quyết định bằng cách dữ liệu được lưu trữ sẽ được truy cập hoặc thêm vào.

Về tiêu thụ bộ nhớ. Bạn dường như đang trộn một số thứ. Một mảng sẽ chỉ cung cấp cho bạn một đoạn bộ nhớ liên tục cho loại dữ liệu mà bạn có. Đừng quên rằng java có các kiểu dữ liệu cố định: boolean, char, int, long, float và Object (bao gồm tất cả các đối tượng, thậm chí một mảng là một Object). Điều đó có nghĩa là nếu bạn khai báo một chuỗi các chuỗi Chuỗi [1000] hoặc MyObject myObjects [1000], bạn chỉ nhận được 1000 hộp bộ nhớ đủ lớn để lưu trữ vị trí (tham chiếu hoặc con trỏ) của các đối tượng. Bạn không nhận được 1000 hộp bộ nhớ đủ lớn để vừa với kích thước của các đối tượng. Đừng quên rằng các đối tượng của bạn được tạo trước tiên bằng "mới". Đây là khi cấp phát bộ nhớ được thực hiện và sau đó một tham chiếu (địa chỉ bộ nhớ của chúng) được lưu trữ trong mảng. Đối tượng không được sao chép vào mảng chỉ tham chiếu.


1

Tôi không nghĩ nó tạo ra sự khác biệt thực sự cho String. Điều tiếp giáp trong một chuỗi các chuỗi là các tham chiếu đến các chuỗi, chính các chuỗi được lưu trữ tại các vị trí ngẫu nhiên trong bộ nhớ.

Mảng so với Danh sách có thể tạo sự khác biệt cho các kiểu nguyên thủy, không phải cho các đối tượng. NẾU bạn biết trước số lượng phần tử và không cần linh hoạt, một loạt hàng triệu số nguyên hoặc nhân đôi sẽ hiệu quả hơn trong bộ nhớ và tốc độ nhanh hơn một danh sách, vì thực sự chúng sẽ được lưu trữ liên tục và truy cập ngay lập tức. Đó là lý do tại sao Java vẫn sử dụng các mảng ký tự cho chuỗi, mảng int cho dữ liệu hình ảnh, v.v.


1

Mảng nhanh hơn - tất cả bộ nhớ được phân bổ trước.


1

Rất nhiều microbenchmark được đưa ra ở đây đã tìm thấy số lượng vài nano giây cho những thứ như mảng / ArrayList đọc. Điều này khá hợp lý nếu mọi thứ đều nằm trong bộ đệm L1 của bạn.

Bộ nhớ cache cấp cao hơn hoặc truy cập bộ nhớ chính có thể có thứ tự độ lớn của khoảng 10nS-100nS, so với 1nS cho bộ đệm L1. Truy cập vào một ArrayList có thêm một phần nhớ bộ nhớ và trong một ứng dụng thực tế, bạn có thể trả chi phí này từ hầu như không bao giờ đến mọi lúc, tùy thuộc vào mã của bạn đang làm gì giữa các lần truy cập. Và, tất nhiên, nếu bạn có nhiều ArrayLists nhỏ, điều này có thể thêm vào việc sử dụng bộ nhớ của bạn và làm cho nhiều khả năng bạn sẽ bị mất bộ nhớ cache.

Các poster ban đầu dường như chỉ sử dụng một và truy cập rất nhiều nội dung trong một thời gian ngắn, vì vậy nó sẽ không gặp khó khăn lớn. Nhưng nó có thể khác với những người khác và bạn nên cẩn thận khi giải thích các vi khuẩn.

Tuy nhiên, các chuỗi Java rất lãng phí, đặc biệt là nếu bạn lưu trữ nhiều chuỗi nhỏ (chỉ cần nhìn vào chúng bằng một bộ phân tích bộ nhớ, có vẻ như> 60 byte cho một chuỗi vài ký tự). Một mảng các chuỗi có một hướng đến đối tượng String và một chuỗi khác từ đối tượng String đến char [] chứa chính chuỗi đó. Nếu bất cứ điều gì sẽ thổi tung bộ nhớ cache L1 của bạn thì đây là kết hợp với hàng ngàn hoặc hàng chục nghìn Chuỗi. Vì vậy, nếu bạn nghiêm túc - thực sự nghiêm túc - về việc loại bỏ càng nhiều hiệu suất càng tốt thì bạn có thể xem xét việc đó khác đi. Bạn có thể, nói, giữ hai mảng, một char [] với tất cả các chuỗi trong đó, lần lượt từng chuỗi và một int [] có độ lệch cho điểm bắt đầu. Đây sẽ là một PITA để làm bất cứ điều gì với, và bạn gần như chắc chắn không cần nó. Và nếu bạn làm, bạn '


0

Nó phụ thuộc vào cách bạn phải truy cập nó.

Sau khi lưu trữ, nếu bạn chủ yếu muốn thực hiện thao tác tìm kiếm, với ít hoặc không chèn / xóa, thì hãy tìm Array (vì tìm kiếm được thực hiện trong O (1) trong mảng, trong khi thêm / xóa có thể cần sắp xếp lại các phần tử) .

Sau khi lưu trữ, nếu mục đích chính của bạn là thêm / xóa chuỗi, với ít hoặc không có thao tác tìm kiếm, thì hãy vào Danh sách.


0

ArrayList bên trong sử dụng đối tượng mảng để thêm (hoặc lưu trữ) các phần tử. Nói cách khác, ArrayList được hỗ trợ bởi cấu trúc dữ liệu Array. Mảng của ArrayList có thể thay đổi kích thước (hoặc động).

Mảng nhanh hơn Mảng vì ArrayList sử dụng mảng bên trong. nếu chúng ta có thể trực tiếp thêm các phần tử trong Array và gián tiếp thêm phần tử trong Array thông qua ArrayList thì cơ chế trực tiếp sẽ nhanh hơn cơ chế gián tiếp.

Có hai phương thức add () bị quá tải trong lớp ArrayList:
1 . add(Object) : thêm đối tượng vào cuối danh sách.
2 . add(int index , Object ) : chèn đối tượng đã chỉ định vào vị trí đã chỉ định trong danh sách.

Làm thế nào kích thước của ArrayList phát triển linh hoạt?

public boolean add(E e)        
{       
     ensureCapacity(size+1);
     elementData[size++] = e;         
     return true;
}

Điểm quan trọng cần lưu ý từ mã trên là chúng tôi đang kiểm tra dung lượng của ArrayList, trước khi thêm phần tử. Đảm bảoCapacity () xác định kích thước hiện tại của các phần tử bị chiếm đóng và kích thước tối đa của mảng là bao nhiêu. Nếu kích thước của các phần tử được lấp đầy (bao gồm phần tử mới được thêm vào lớp ArrayList) lớn hơn kích thước tối đa của mảng thì tăng kích thước của mảng. Nhưng kích thước của mảng không thể tăng động. Vì vậy, những gì xảy ra trong nội bộ là Array mới được tạo ra với dung lượng

Đến Java 6

int newCapacity = (oldCapacity * 3)/2 + 1;

(Cập nhật) Từ Java 7

 int newCapacity = oldCapacity + (oldCapacity >> 1);

Ngoài ra, dữ liệu từ mảng cũ được sao chép vào mảng mới.

Có các phương thức trên cao trong ArrayList đó là lý do tại sao Array nhanh hơn ArrayList.


0

Mảng - Sẽ luôn tốt hơn khi chúng ta phải đạt được kết quả tìm nạp nhanh hơn

Danh sách- Thực hiện kết quả khi chèn và xóa vì chúng có thể được thực hiện trong O (1) và điều này cũng cung cấp các phương pháp để thêm, tìm nạp và xóa dữ liệu một cách dễ dàng. Dễ sử dụng hơn nhiều.

Nhưng luôn nhớ rằng việc tìm nạp dữ liệu sẽ nhanh khi vị trí chỉ mục trong mảng nơi dữ liệu được lưu trữ - được biết đến.

Điều này có thể đạt được tốt bằng cách sắp xếp các mảng. Do đó, điều này làm tăng thời gian tìm nạp dữ liệu (nghĩa là lưu trữ dữ liệu + sắp xếp dữ liệu + tìm kiếm vị trí tìm thấy dữ liệu). Do đó, điều này làm tăng độ trễ bổ sung để tìm nạp dữ liệu từ mảng thậm chí chúng có thể tốt để tìm nạp dữ liệu sớm hơn.

Do đó, điều này có thể được giải quyết với cấu trúc dữ liệu trie hoặc cấu trúc dữ liệu ternary. Như đã thảo luận ở trên, cấu trúc dữ liệu trie sẽ rất hiệu quả trong việc tìm kiếm dữ liệu, việc tìm kiếm một từ đặc biệt có thể được thực hiện ở cường độ O (1). Khi thời gian có nghĩa là; nếu bạn phải tìm kiếm và truy xuất dữ liệu nhanh chóng, bạn có thể sử dụng cấu trúc dữ liệu trie.

Nếu bạn muốn không gian bộ nhớ của bạn được tiêu thụ ít hơn và bạn muốn có hiệu suất tốt hơn thì hãy đi với cấu trúc dữ liệu ternary. Cả hai đều phù hợp để lưu trữ số lượng lớn các chuỗi (ví dụ: như các từ có trong từ điển).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.