ArrayList ๋ด๋ถ๊ตฌํ ๊ฐ๋จํ๊ฒ ์ดํด๋ณด๊ธฐ (feat. Array)
๊ฐ์
ArrayList๋ฅผ ์์ฃผ ์ฌ์ฉํ๋ ํธ์ธ๋ฐ ArrayList๋ ์ Array์ ๋ค๋ฅด๊ฒ ์ด๊ธฐ ์ฌ์ด์ฆ๋ฅผ ์ ํด์ฃผ์ง ์์๋ ๋๊ณ , ๋ฌดํ์ ์ผ๋ก ์์๊ฐ ์ถ๊ฐ๋๋ ๊ฒ ์ ๋๋ค. ์ด ๋ถ๋ถ์ด ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํ๋ฉด์๋ ๋ ์๋ฌธ์ด์์ต๋๋ค. ์ค๋์ ArrayList๋ฅผ ๊ฐ๋จํ๊ฒ ํด๋ถํด๋ณผ ๊ฒ ์ ๋๋ค. ์์ ์ถ๊ฐํ๋ ๋ถ๋ถ์ ์ค์ ์ ์ผ๋ก ์ดํด๋ณด๊ฒ ์ต๋๋ค.
ArrayList ๋ด๋ถ์ ์ผ๋ก๋ ์ด๋ป๊ฒ ๋์ด์์๊น?
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 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 = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
...
}
๊ฐ์ฅ ๋์ ๋๋ ๋ถ๋ถ๋ค์ ๋ด๋ถ ์์๋ฅผ Object[]
๋ก ๊ตฌํํ ์ ๊ณผ elementData
์ ์ฌ์ด์ฆ๋ฅผ ๋ํ๋ด๋ size
๋ณ์, ๊ทธ๋ฆฌ๊ณ ์ด๊ธฐ ์ฉ๋์ธ DEFAULT_CAPACITY
๊ฐ ๋ณด์
๋๋ค.
๋ฐฐ์ด๋ก ๊ตฌํ๋์ด ์๋๋ฐ ์ด๋ป๊ฒ ๋ฌดํ์ ์ผ๋ก ์์ ์ถ๊ฐ ๋ ๊น?
ArrayList๋ฅผ ์์ฑํด์ add ๋ฉ์๋๋ฅผ ํธ์ถํด๋ณด์ ๊ธฐ์ต์ด ์์ผ์ค๊ฒ๋๋ค. ๋ฐฐ์ด์ ์ด๊ธฐ์ฉ๋์ ์ ํด์ค์ผํ๊ณ ์ด๊ธฐ์ฉ๋์ ์ด๊ณผํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
๊ทธ๋ฐ๋ฐ ์ด๋ป๊ฒ ArrayList๋ ์ด๊ธฐ ์ฉ๋์ด 10
์ผ๋ก ์ ํด ์ก๋๋ฐ๋ ๋ถ๊ตฌํ๊ณ ๋ฌดํ์ ์์๊ฐ ์ถ๊ฐ๋ ์ ์์๊น์?
๊ทธ ์ด์ ๋ฅผ ์๊ธฐ ์ํด์๋ ArrayList์ add(E e)
๋ฉ์๋๋ฅผ ์ดํด๋ด์ผ ํฉ๋๋ค.
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
size๋ ํ์ฌ ๋ฐฐ์ด์ ์ฌ์ด์ฆ์
๋๋ค. ํ์ฌ ์ฌ์ด์ฆ์ 1์ ๋ํด์ ensureCapacityInternal
๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ์์ต๋๋ค.
๊ทธ๋ผ ensureCapacityInternal ๋ฉ์๋๋ฅผ ์ดํด๋ณผ๊น์?
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
ํ์ฌ elementData์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ง๊ณ calculateCapacity๋ฅผ ํธ์ถํฉ๋๋ค. ๋ฉ์๋ ๋ช ์ผ๋ก ๋ฏธ๋ค๋ดค์ ๋ ์ธ์คํด์ค๊ฐ ๊ฐ์ง๊ณ ์๋ ๋ฐฐ์ด๊ณผ ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ง๊ณ ์ ์ ํ ๋ฐฐ์ด์ ์ฉ๋์ ๊ณ์ฐํด์ค ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค.
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
calculateCapacity ๋ฉ์๋์ ๋๋ค. ๋ค์ ๋ก์ง์ ๊ฑฐ์น๋ฉด์ ์ํ๋ฉ๋๋ค.
- ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์
elementData
๊ฐ ๋น์ด์๋ ๋ฐฐ์ด์ธ์ง ํ์ธํฉ๋๋ค - ๋ง์ฝ ๋น์ด์๋ค๋ฉด(true) โ
DEFAULT_CAPACITY(=10)
์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์minCapacity
๋ ์ค ํฐ ์๋ฅผ ๋ฐํํฉ๋๋ค. - ๋น์ด์์ง ์๋ค๋ฉด(false) โ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์
minCapacity
๋ฅผ ๊ทธ๋๋ก ๋ฐํํฉ๋๋ค.
์! ๋ค์ ensureCapacityInternal ๋ฉ์๋๋ก ๋์์ต๋๋ค.
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity
๋ฉ์๋์์ ์ ํด์ค ์ฉ๋์ ๊ฐ์ง๊ณ ensureExplicityCapacity
๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค.
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
๋จผ์ modCount๋ผ๋ ๋ณ์(์ด elementData๊ฐ ๋ช๋ฒ ์์ ๋๋์ง์ ๋ํ ํ์๋ฅผ ์ ์ฅํ๋ ๋ณ์)๋ฅผ ์ฆ๊ฐ์ํค๊ณ , ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ minCapacity๊ฐ ํ์ฌ elementData์ ์ฌ์ด์ฆ ๋ณด๋ค ํฌ๋ค๋ฉด grow
๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค.
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
grow ํจ์์์๋ ๋ค์ ๋ก์ง์ ์ํํฉ๋๋ค.
- ํ์ฌ ๋ฐฐ์ด์ ์ฌ์ด์ฆ๋ฅผ oldCapacity ๋ณ์์ ํ ๋น
- oldCapacity ๋ณ์์ oldCapacity ๋นํธ ์ฌํํธํ ์(oldCapacity์ 1/2 ํ ๋๋จธ์ง ๋ฒ๋ฆผํ ์)๋ฅผ ๋ํ ๊ฐ์ newCapacity์ ํ ๋นํฉ๋๋ค.
- ๋ง์ฝ newCapacity๊ฐ minCapacity๋ณด๋ค ์์ผ๋ฉด newCapacity๋ minCapacity๊ฐ ๋๋ค.
- ๋ง์ฝ newCapacity๊ฐ ์ต๋์ฌ์ด์ฆ(
Integer.MAX_VALUE - 8
) ๋ณด๋ค ํฌ๋ค๋ฉด, hugeCapacity ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค. - ์ต์ข ์ ์ผ๋ก ์ ํด์ง newCapacity๋ฅผ ๊ฐ์ง๋ ์๋ก์ด ๋ฐฐ์ด์ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ์ฌ elementData์ ์ ์ฅํฉ๋๋ค.
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
hugeCapacity ๋ฉ์๋์ ๋๋ค. 0๋ณด๋ค ์์ผ๋ฉด ์์ธ๋ฅผ ๋์ง๊ณ , ํ๋ผ๋ฏธํฐ๊ฐ ๋ฐฐ์ด์ ์ต๋ ์ฌ์ด์ฆ๋ณด๋ค ํฌ๋ฉด Integer์ ์ต๋ ๊ฐ์ ๋ฐํํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด ๋ฐฐ์ด์ ์ต๋ ์ฌ์ด์ฆ๋ฅผ ๋ฐํํฉ๋๋ค.
OutOfMemoryError๋ฅผ ๋์ง๋ ๋ถ๋ถ์ ๋ณด๋ฉด, ๋งจ ์ฒ์ add ํจ์์์ size + 1์ ํ๋ ๋ถ๋ถ์ด ์์ต๋๋ค. ๋ง์ฝ ๊ทธ๋์ size๊ฐ Integer.MAX_VALUE
์๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
๋ค, ๋ฐ๋ก ์ค๋ฒํ๋ก์ฐ๊ฐ ๋ผ์ ์์๊ฐ์ ๊ฐ์ง๊ฒ ๋ ๊ฒ ์
๋๋ค. ์ด๋ฌํ ์ํฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด์ ๋ฐฉ์ด์ฝ๋๋ฅผ ์ฝ์
ํ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋ผ ์ฐ๋ฆฌ๋ ArrayList์ ์ต๋ ์ฌ์ด์ฆ๋ Integer.MAX_VALUE
๋ผ๋ ๊ฒ์ ์ง์ํ ์ ์์ต๋๋ค.
๋ง์น๋ฉฐ..
๊ฐ๋จํ๊ฒ ArrayList ๋ด๋ถ์ ์ด๋ค ๋ฉค๋ฒ ๋ณ์๊ฐ ์ ์ธ๋์ด ์๋์ง, ์ด๋ป๊ฒ ์ด๊ธฐ ์ฌ์ด์ฆ๋ฅผ ์ง์ ํด์ฃผ์ง ์์๋๋ฐ ๊ฐ๋ณ์ ์ผ๋ก ์ฌ์ด์ฆ๊ฐ ๋์ด๋๋์ง add ๋ฉ์๋๋ฅผ ํ๋ํ๋ ์ดํด๋ณด๋ฉด์ ๊ทธ ์ด์ ๋ฅผ ์์๋ณด์์ต๋๋ค. ๊ทธ๋์ ์๋ฌด ์๊ฐ์์ด ํธ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ArrayList๋ฅผ ์ฌ์ฉํ๋๋ฐ์, ๋ด๋ถ์ ์ผ๋ก ์ด๋ป๊ฒ ๋์ํ๋์ง๋ฅผ ์ดํด๋ณธ ๋ค์๋ ๋ฆฌ์คํธ์ ๊ฐ์ ์ถ๊ฐํ ๋ ๋ฆฌ์์ค ๋ญ๋น๊ฐ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์ด ์ค์ํ ๋ถ๋ถ์์๋ ์ฌ์ฉ์ ์ง์ ํด์ผ๊ฒ ๋ค๊ณ ๊นจ๋ฌ์์ต๋๋ค.
๊ธด ๊ธ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ์ ์ ๊ธ์ด ๋ง์ ๋ถ๋ค๊ป ๋์์ด ๋์์ผ๋ฉด ์ข๊ฒ ์ต๋๋ค. ํ๋ฆฐ ๋ด์ฉ์ด ์๋ค๋ฉด ์ธ์ ๋ ํธํ๊ฒ ๋ง์ํด์ฃผ์๋ฉด ๊ฐ์ฌ ๋๋ฆฌ๊ฒ ์ต๋๋ค! ๊ฐ์ฌํฉ๋๋ค!