Trong Java 8, tại sao dung lượng mặc định của ArrayList bây giờ là 0?


93

Như tôi nhớ lại, trước Java 8, dung lượng mặc định ArrayListlà 10.

Đáng ngạc nhiên là nhận xét về hàm tạo (void) mặc định vẫn cho biết: Constructs an empty list with an initial capacity of ten.

Từ ArrayList.java:

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

...

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

Câu trả lời:


105

Về mặt kỹ thuật, nó 10, không phải là 0, nếu bạn thừa nhận việc khởi tạo mảng hỗ trợ một cách lười biếng. Xem:

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

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

Ở đâu

/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

Những gì bạn đang đề cập đến chỉ là đối tượng mảng ban đầu có kích thước bằng 0 được chia sẻ giữa tất cả ArrayListcác đối tượng trống ban đầu . Tức là dung lượng của 10được đảm bảo một cách lười biếng , một sự tối ưu hóa cũng có trong Java 7.

Phải thừa nhận rằng hợp đồng xây dựng không hoàn toàn chính xác. Có lẽ đây là nguồn gốc của sự nhầm lẫn ở đây.

Lý lịch

Đây là một E-Mail của Mike Duigou

Tôi đã đăng một phiên bản cập nhật của bản vá ArrayList và HashMap trống.

http://cr.openjdk.java.net/~mduigou/JDK-7143928/1/webrev/

Việc triển khai sửa đổi này không giới thiệu trường mới cho cả hai lớp. Đối với ArrayList, việc phân bổ lười biếng của mảng hỗ trợ chỉ xảy ra nếu danh sách được tạo ở kích thước mặc định. Theo nhóm phân tích hiệu suất của chúng tôi, khoảng 85% phiên bản ArrayList được tạo ở kích thước mặc định, do đó, việc tối ưu hóa này sẽ có hiệu lực đối với phần lớn các trường hợp.

Đối với HashMap, việc sử dụng quảng cáo được thực hiện bằng trường ngưỡng để theo dõi kích thước ban đầu được yêu cầu cho đến khi cần mảng nhóm. Ở phía đọc, trường hợp bản đồ trống được kiểm tra với isEmpty (). Trên kích thước ghi, so sánh với (table == EMPTY_TABLE) được sử dụng để phát hiện sự cần thiết phải tăng cường mảng xô. Trong readObject, có thêm một số công việc để cố gắng chọn một dung lượng ban đầu hiệu quả.

Từ: http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-April/015585.html


4
Theo bugs.java.com/bugdatabase/view_bug.do?bug_id=7143928 nó dẫn đến giảm thiểu sử dụng heap và cải thiện thời gian đáp ứng (số lượng cho hai appications được hiển thị)
Thomas Kläger

3
@khelwood: ArrayList không thực sự "báo cáo" dung lượng của nó, ngoài việc thông qua Javadoc này: không có getCapacity()phương pháp hoặc bất cứ điều gì tương tự. (Điều đó nói rằng, một cái gì đó giống như ensureCapacity(7)là không chọn cho ArrayList được khởi tạo mặc định, vì vậy tôi đoán chúng ta thực sự phải hoạt động như thể dung lượng ban đầu của nó thực sự là 10.)
ruakh

10
Đào đẹp. Dung lượng ban đầu mặc định thực sự không phải là 0, mà là 10, với trường hợp mặc định được cấp phát một cách lười biếng như một trường hợp đặc biệt. Bạn có thể quan sát điều này nếu bạn liên tục thêm các phần tử vào một hàm ArrayListtạo no-arg so với việc chuyển 0 cho hàm inttạo và nếu bạn xem kích thước mảng bên trong một cách phản xạ hoặc trong một trình gỡ lỗi. Trong trường hợp mặc định, mảng nhảy từ độ dài 0 đến 10, sau đó đến 15, 22, theo tốc độ tăng 1,5 lần. Vượt qua 0 khi công suất ban đầu dẫn đến tăng trưởng từ 0 lên 1, 2, 3, 4, 6, 9, 13, 19 ....
Stuart Marks

13
Tôi là Mike Duigou, tác giả của thay đổi và email được trích dẫn và tôi chấp thuận thông báo này. 🙂 Như Stuart cho biết động lực chủ yếu là tiết kiệm không gian hơn là hiệu suất mặc dù cũng có một chút lợi ích về hiệu suất do thường xuyên tránh tạo mảng hỗ trợ.
Mike Duigou

4
@assylias:; ^) không, nó vẫn có vị trí của nó vì một singleton emptyList()vẫn tiêu tốn ít bộ nhớ hơn một số ArrayListtrường hợp trống . Nó chỉ ít quan trọng hơn bây giờ và do đó không cần thiết ở mọi nơi, đặc biệt là không cần thiết ở những nơi có nhiều khả năng thêm các phần tử vào thời gian sau. Cũng nên nhớ rằng đôi khi bạn muốn có một danh sách trống bất biến và sau đó emptyList()là cách để thực hiện.
Holger

23

Trong java 8 dung lượng mặc định của ArrayList là 0 cho đến khi chúng ta thêm ít nhất một đối tượng vào đối tượng ArrayList (Bạn có thể gọi nó là khởi tạo lười biếng).

Bây giờ câu hỏi là tại sao thay đổi này đã được thực hiện trong JAVA 8?

Trả lời là để tiết kiệm tiêu thụ bộ nhớ. Hàng triệu đối tượng danh sách mảng được tạo trong các ứng dụng java thời gian thực. Kích thước mặc định của 10 đối tượng có nghĩa là chúng tôi phân bổ 10 con trỏ (40 hoặc 80 byte) cho mảng bên dưới khi tạo và điền chúng bằng null. Một mảng trống (chứa đầy null) chiếm nhiều bộ nhớ.

Khởi tạo lười biếng sẽ trì hoãn việc tiêu thụ bộ nhớ này cho đến khi bạn thực sự sử dụng danh sách mảng.

Vui lòng xem mã dưới đây để được trợ giúp.

ArrayList al = new ArrayList();          //Size:  0, Capacity:  0
ArrayList al = new ArrayList(5);         //Size:  0, Capacity:  5
ArrayList al = new ArrayList(new ArrayList(5)); //Size:  0, Capacity:  0
al.add( "shailesh" );                    //Size:  1, Capacity: 10

public static void main( String[] args )
        throws Exception
    {
        ArrayList al = new ArrayList();
        getCapacity( al );
        al.add( "shailesh" );
        getCapacity( al );
    }

    static void getCapacity( ArrayList<?> l )
        throws Exception
    {
        Field dataField = ArrayList.class.getDeclaredField( "elementData" );
        dataField.setAccessible( true );
        System.out.format( "Size: %2d, Capacity: %2d%n", l.size(), ( (Object[]) dataField.get( l ) ).length );
}

Response: - 
Size:  0, Capacity:  0
Size:  1, Capacity: 10

Bài viết Dung lượng mặc định của ArrayList trong Java 8 giải thích chi tiết về nó.


7

Nếu thao tác đầu tiên được thực hiện với ArrayList là chuyển addAllmột tập hợp có hơn mười phần tử, thì bất kỳ nỗ lực nào nhằm tạo ra một mảng mười phần tử ban đầu để chứa nội dung của ArrayList sẽ bị ném ra ngoài cửa sổ. Bất cứ khi nào một thứ gì đó được thêm vào ArrayList, cần phải kiểm tra xem kích thước của danh sách kết quả có vượt quá kích thước của kho lưu trữ sao lưu hay không; việc cho phép lưu trữ sao lưu ban đầu có kích thước bằng 0 thay vì 10 sẽ khiến thử nghiệm này không thành công thêm một lần nữa trong thời gian tồn tại của danh sách có thao tác đầu tiên là "thêm" sẽ yêu cầu tạo mảng mười mục ban đầu, nhưng chi phí đó là ít hơn chi phí tạo một mảng mười mục không bao giờ hết được sử dụng.

Điều đó đã được nói, có thể cải thiện hiệu suất hơn nữa trong một số ngữ cảnh nếu có quá tải "addAll" chỉ định số lượng mục (nếu có) có thể sẽ được thêm vào danh sách sau mục hiện tại và có thể sử dụng điều đó để ảnh hưởng đến hành vi phân bổ của nó. Trong một số trường hợp, mã thêm một vài mục cuối cùng vào danh sách sẽ có một ý tưởng khá hay rằng danh sách sẽ không bao giờ cần bất kỳ khoảng trống nào ngoài đó. Có nhiều tình huống trong đó danh sách sẽ được điền một lần và không bao giờ được sửa đổi sau đó. Nếu tại điểm mã biết rằng kích thước cuối cùng của một danh sách sẽ là 170 phần tử, thì danh sách đó có 150 phần tử và một kho dự trữ có kích thước 160,


Điểm rất tốt về addAll(). Đó là một cơ hội khác để cải thiện hiệu quả xung quanh lần đầu tiên.
kevinarpe

@kevinarpe: Tôi ước thư viện của Java đã được thiết kế theo một số cách khác để các chương trình chỉ ra cách mọi thứ có thể được sử dụng. Ví dụ, kiểu cũ của chuỗi con là tệ hại đối với một số trường hợp sử dụng, nhưng tuyệt vời đối với những trường hợp khác. Nếu có các chức năng riêng biệt cho "chuỗi con có khả năng tồn tại lâu hơn chuỗi gốc" và "chuỗi con không có khả năng tồn tại lâu hơn chức năng gốc" và mã sử dụng đúng hàm 90% thời gian, tôi sẽ nghĩ rằng những hàm đó có thể hoạt động tốt hơn nhiều so với triển khai chuỗi cũ hoặc mới.
supercat

3

Câu hỏi là 'tại sao?'.

Kiểm tra hồ sơ bộ nhớ (ví dụ: ( https://www.yourkit.com/docs/java/help/inspection_mem.jsp#sparse_arrays ) cho thấy rằng các mảng trống (chứa đầy null) chiếm rất nhiều bộ nhớ.

Kích thước mặc định của 10 đối tượng có nghĩa là chúng tôi phân bổ 10 con trỏ (40 hoặc 80 byte) cho mảng bên dưới khi tạo và điền chúng bằng null. Các ứng dụng java thực tạo ra hàng triệu danh sách mảng.

Sửa đổi được giới thiệu loại bỏ ^ W hoãn việc tiêu thụ bộ nhớ này cho đến thời điểm bạn thực sự sử dụng danh sách mảng.


Hãy sửa "tiêu thụ" bằng "chất thải". Liên kết bạn cung cấp không có nghĩa là chúng bắt đầu ngốn bộ nhớ ở khắp mọi nơi, chỉ là các mảng có phần tử rỗng sẽ lãng phí bộ nhớ được cấp cho chúng, một cách không cân xứng. "Tiêu thụ" ngụ ý họ sử dụng bộ nhớ vượt quá mức phân bổ của họ một cách kỳ diệu, điều này không đúng trong trường hợp này.
Mechalynx

0

Kích thước mặc định của ArrayList trong JAVA 8 là 10. Thay đổi duy nhất được thực hiện trong JAVA 8 là nếu người viết mã thêm các phần tử nhỏ hơn 10 thì các vị trí trống còn lại của danh sách mảng không được chỉ định thành null. Nói như vậy bởi vì bản thân tôi đã trải qua tình huống này và nhật thực khiến tôi nhìn vào sự thay đổi này của JAVA 8.

Bạn có thể biện minh cho sự thay đổi này bằng cách xem ảnh chụp màn hình bên dưới. Trong đó, bạn có thể thấy rằng kích thước ArrayList được chỉ định là 10 trong Object [10] nhưng số phần tử được hiển thị chỉ là 7. Các phần tử có giá trị Rest null không được hiển thị ở đây. Trong JAVA 7 ảnh chụp màn hình dưới đây giống nhau chỉ với một thay đổi duy nhất đó là các phần tử giá trị null cũng được hiển thị mà người lập trình cần viết mã để xử lý các giá trị null nếu anh ta đang lặp lại danh sách mảng hoàn chỉnh trong khi trong JAVA 8, gánh nặng này được loại bỏ khỏi người đứng đầu lập trình viên / nhà phát triển.

Liên kết ảnh chụp màn hình.


0

Sau câu hỏi trên, tôi đã xem qua ArrayList Document của Java 8. Tôi thấy kích thước mặc định vẫn chỉ là 10.

Vui lòng xem bên dưới

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.