2010-04-20 43 views
5

Trong lớp Cấu trúc dữ liệu của tôi, chúng tôi đã nghiên cứu lớp Java ArrayList và cách nó phát triển mảng cơ bản khi người dùng thêm nhiều phần tử hơn. Điều đó được hiểu. Tuy nhiên, tôi không thể tìm ra cách chính xác lớp này giải phóng bộ nhớ khi có nhiều phần tử bị loại bỏ khỏi danh sách. Nhìn vào nguồn, có ba phương pháp loại bỏ các phần tử:Java: Cách ArrayList quản lý bộ nhớ

public E remove(int index) { 
RangeCheck(index); 

modCount++; 
E oldValue = (E) elementData[index]; 

int numMoved = size - index - 1; 
if (numMoved > 0) 
    System.arraycopy(elementData, index+1, elementData, index, 
     numMoved); 
elementData[--size] = null; // Let gc do its work 

return oldValue; 
} 

public boolean remove(Object o) { 
if (o == null) { 
      for (int index = 0; index < size; index++) 
    if (elementData[index] == null) { 
     fastRemove(index); 
     return true; 
    } 
} else { 
    for (int index = 0; index < size; index++) 
    if (o.equals(elementData[index])) { 
     fastRemove(index); 
     return true; 
    } 
     } 
return false; 
} 


private void fastRemove(int index) { 
     modCount++; 
     int numMoved = size - index - 1; 
     if (numMoved > 0) 
      System.arraycopy(elementData, index+1, elementData, index, 
          numMoved); 
     elementData[--size] = null; // Let gc do its work 
} 

Không ai trong số đó giảm mảng dữ liệu. Tôi thậm chí còn bắt đầu đặt câu hỏi nếu bộ nhớ miễn phí lên bao giờ xảy ra, nhưng các thử nghiệm thực nghiệm cho thấy rằng nó. Vì vậy, phải có một số cách khác nó được thực hiện, nhưng ở đâu và như thế nào? Tôi đã kiểm tra các lớp cha mẹ cũng như không thành công.

+0

Vui lòng định dạng lại mã của bạn. Stackoverflow không hiểu [code], vui lòng chỉnh sửa bài đăng của bạn, chọn đoạn mã và nhấn nút "Mã" để định dạng nó. – Behrang

+0

Vâng, đây là bài đăng đầu tiên của tôi và tôi đang trong quá trình tìm hiểu cách sử dụng định dạng. Có người đã giúp tôi mặc dù :) – cka3o4nik

Trả lời

9

Chúng không làm giảm mảng cơ bản. Họ chỉ đơn giản là giảm kích thước. Lý do cho điều này là nếu bạn có 1000 phần tử trong một mảng và xóa 1, tại sao tái phân bổ và sao chép mảng? Đó là cực kỳ lãng phí cho rất ít được.

Về cơ bản Java ArrayList s có hai thuộc tính quan trọng và điều quan trọng là phải hiểu chúng khác nhau:

  • kích thước: bao nhiêu yếu tố này là notionally trong List; và

  • công suất: số lượng phần tử có thể phù hợp với mảng cơ bản.

Khi mở rộng khoảng 50% ngay cả khi bạn chỉ thêm một phần tử. Đây là một nguyên tắc tương tự ngược lại. Về cơ bản nó đi xuống này: reallocating mảng và sao chép các giá trị là (tương đối) đắt tiền. Vì vậy, nhiều như vậy mà bạn muốn giảm thiểu nó xảy ra. Miễn là kích thước tiêu chuẩn là với một nhà máy khoảng 2 của kích thước mảng, nó chỉ là không đáng lo ngại về.

+0

Vâng, tôi hiểu rằng sẽ rất ngớ ngẩn khi phân bổ lại sau khi xóa một phần tử. Nhưng nếu tôi thêm một triệu phần tử, và sau đó loại bỏ tất cả chúng nhưng một, và không bao giờ thêm bất cứ điều gì cho đến khi chương trình kết thúc. Nó sẽ là một sự lãng phí bộ nhớ để giữ một mảng triệu tế bào thay vì kích thước mặc định. – cka3o4nik

+2

@ cka3o4nik: nó chỉ là một mảng tham chiếu. Một triệu người trong số họ vẫn chỉ mất khoảng 4MB - không thực sự đáng lo ngại về một trường hợp hiếm hoi như vậy. –

+2

@ cka304nik như Michael nói, việc giảm kích thước mảng không được xem là một vấn đề lớn. Nếu bạn muốn làm điều này tuy nhiên bạn có thể gọi 'ArrayList.trimToSize()'. – cletus

1

Tôi phải có một cái nhìn khác về mã nguồn của ArrayList, nhưng remove loại bỏ đối tượng khỏi mảng, và sau đó nếu đối tượng không được tham chiếu bởi bất kỳ đối tượng nào khác, GC có thể xóa đối tượng đó. Nhưng kích thước mảng không giảm.

0

Không có nhiều lợi ích bằng cách thay đổi kích thước mảng nội bộ ArrayList, thậm chí bạn đang sử dụng ArrayList để giữ một đối tượng lớn.

List<LargeObject> list = new ArrayList<LargetObject>(); 

danh sách sẽ chỉ giữ tham chiếu đến cá thể LargeObject và không giữ chính đối tượng LargeObject.

Tham chiếu không tiêu tốn nhiều không gian. (Hãy suy nghĩ nó như con trỏ trong C)

+0

Thật kỳ lạ khi mọi người sợ con trỏ trong C và rất tốt với ý tưởng * tham chiếu * trong Java. – Pijusn

0

Kích thước mảng không bao giờ được giảm tự động. Rất hiếm khi thực sự có một danh sách được lấp đầy đầu tiên với một số lượng lớn các nguyên tố, sau đó đã làm trống nhưng vẫn giữ nguyên. Và hãy nhớ rằng phải có đủ bộ nhớ để giữ danh sách (chỉ bao gồm các tham chiếu) và các phần tử của nó - không chắc rằng bộ nhớ được tiêu thụ bởi danh sách trống sẽ là một vấn đề.

Nếu bạn thực sự gặp phải thuật toán kỳ quái đủ để điều này trở thành vấn đề, bạn vẫn có thể giải phóng bộ nhớ bằng cách gọi trimToSize() theo cách thủ công.

+0

Điểm tốt, cảm ơn. – cka3o4nik

4

Một ArrayList không tự động thu nhỏ lại, theo như tôi biết. Tuy nhiên, bạn có thể nói điều gì đó như:

ArrayList al = new ArrayList(); 

// fill the list for demo's sake 
for (int i = 0; i < 1000000; ++i) 
{ 
    al.add(i); 
} 

// now remove all but one element 
al.removeRange(1, al.size()); 

// this should shrink the array back to a decent size 
al.trimToSize(); 

Lưu ý, lượng bộ nhớ có thể sẽ không thay đổi cho đến khi chạy lại GC.

+0

Tôi đã kết thúc sao chép mã nguồn ArrayList vào dự án thử nghiệm của mình và thêm phương thức getCapacity() để vào mảng cơ bản. Như các bạn đã dự đoán, nó không bao giờ đi xuống trừ khi tôi yêu cầu nó bằng cách trimToSize() theo cách thủ công. Cảm ơn. – cka3o4nik

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