2010-04-20 21 views
5

W mojej klasie Data Structures badamy klasę Java ArrayList oraz sposób, w jaki rośnie ona w podstawowej tablicy, gdy użytkownik dodaje więcej elementów. To jest zrozumiałe. Nie mogę jednak dowiedzieć się, jak dokładnie ta klasa zwalnia pamięć, gdy wiele elementów zostanie usuniętych z listy. Patrząc na źródło, istnieją trzy metody usuwania elementów:Java: W jaki sposób program ArrayList zarządza pamięcią

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 
} 

Żadna z nich nie zmniejsza tablicy Datastore. Zacząłem nawet zastanawiać się, czy kiedykolwiek nastąpi wolna pamięć, ale testy empiryczne pokazują, że tak. Musi więc istnieć jakiś inny sposób, w jaki jest to zrobione, ale gdzie i jak? Sprawdziłem także klasy rodziców, ale bez powodzenia.

+0

Proszę ponownie sformatować swój kod. Stackoverflow nie rozumie [kodu], edytuj swój post, wybierz fragment kodu i naciśnij przycisk "Kod", aby go sformatować. – Behrang

+0

Tak, to jest mój pierwszy post i właśnie zastanawiałem się, jak używać formatowania. Ktoś mi pomógł :) – cka3o4nik

Odpowiedz

9

Nie zmniejszają podstawowej tablicy. Po prostu zmniejszają rozmiar. Rozumowanie tego jest takie, że jeśli masz 1000 elementów w tablicy i usuń 1, po co przydzielasz i kopiujesz tablicę? To ogromnie marnotrawstwo, z niewielkim zyskiem.

Zasadniczo Java ArrayList s mają dwie ważne właściwości i ważne jest, aby zrozumieć, że są różne:

  • rozmiar: jak wiele elementów są hipotetycznie w List; i

  • pojemność: ile elementy może zmieścić się w podstawowej tablicy.

Gdy ArrayList rozwija, rośnie o około 50% w wielkości nawet jeśli dodajesz tylko jeden element. Jest to podobna zasada w odwrotnej kolejności. Zasadniczo sprowadza się do tego: ponowne przydzielenie tablicy i skopiowanie wartości jest (relatywnie) drogie. Tak bardzo, że chcesz go zminimalizować. Dopóki nominalny rozmiar jest z fabryką około 2 rozmiaru tablicy, to po prostu nie warto się martwić.

+0

Tak, rozumiem, że byłoby głupio przesuwać po usunięciu jednego elementu. Ale co się stanie, jeśli dodaję milion elementów, a następnie usunę wszystkie z nich oprócz jednego i nigdy niczego nie dodam, dopóki program się nie skończy. Byłoby marnotrawstwem pamięci, aby zachować matrycę z milionem komórek zamiast domyślnego rozmiaru. – cka3o4nik

+2

@ cka3o4nik: to tylko tablica odniesień. Milion z nich wciąż zajmuje tylko około 4 MB - naprawdę nie warto martwić się o tak rzadki przypadek. –

+2

@ cka304nik jak mówi Michael, zmniejszenie rozmiaru tablicy nie jest postrzegane jako duży problem. Jeśli chcesz to zrobić, możesz wywołać 'ArrayList.trimToSize()'. – cletus

1

Muszę jeszcze raz spojrzeć na kod źródłowy ArrayList, ale remove usuwa obiekt z tablicy, a następnie, jeśli obiekt nie odwołuje się do żadnych innych obiektów, GC może usunąć ten obiekt. Ale rozmiar tablicy nie jest zmniejszony.

0

Nie ma zbyt wiele korzyści przez zmianę rozmiaru wewnętrznej tablicy ArrayList, nawet jeśli używasz ArrayList do przechowywania dużego obiektu.

lista będzie zawierać odwołanie do instancji LargeObject i nie będzie zawierała samej instancji LargeObject.

Odniesienie nie zajmuje dużo miejsca. (Myśl jak wskaźnik w C)

+0

To dziwne, jak ludzie tak bardzo boją się wskaźników w C i są tak dobrze z pomysłem * reference * w Javie. – Pijusn

0

Rozmiar tablicy nigdy nie jest automatycznie zmniejszany. Bardzo rzadko zdarza się, aby lista była najpierw wypełniona dużą liczbą elementów, a następnie opróżniona, ale nadal przechowywana. I pamiętaj, że musi być wystarczająco dużo pamięci, aby pomieścić listę (która składa się tylko z referencji) i jej elementów - jest mało prawdopodobne, że pamięć zużywana przez pustą listę byłaby problemem.

Jeśli naprawdę napotkasz na tyle dziwaczny algorytm, że stanie się to problemem, możesz zwolnić pamięć ręcznie, dzwoniąc pod numer trimToSize().

+0

Dobre punkty, dzięki. – cka3o4nik

4

ArrayList nie cofa się automatycznie, o ile wiem. Możesz jednak powiedzieć coś w rodzaju:

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(); 

Uwaga, ilość dostępnej pamięci prawdopodobnie nie zmieni się, dopóki GC nie zostanie ponownie uruchomiony.

+0

Zakończyłem kopiowanie kodu źródłowego ArrayList do mojego projektu testowego i dodałem metodę getCapacity(), aby uzyskać dostęp do podstawowej tablicy. Zgodnie z przewidywaniami, nigdy nie spadnie, chyba że ręcznie zażądam przez trimToSize(). Dzięki. – cka3o4nik

Powiązane problemy