๐ŸŒป JAVA/์•Œ๊ณ ๋ฆฌ์ฆ˜, ์ž๋ฃŒ๊ตฌ์กฐ

ArrayList ๋‚ด๋ถ€๊ตฌํ˜„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ดํŽด๋ณด๊ธฐ (feat. Array)

iseunghan 2023. 11. 11. 15:37
๋ฐ˜์‘ํ˜•

๊ฐœ์š”

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๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ์š”, ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ์‚ดํŽด๋ณธ ๋’ค์—๋Š” ๋ฆฌ์ŠคํŠธ์— ๊ฐ’์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ๋ถ€๋ถ„์—์„œ๋Š” ์‚ฌ์šฉ์„ ์ง€์–‘ ํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ €์˜ ๊ธ€์ด ๋งŽ์€ ๋ถ„๋“ค๊ป˜ ๋„์›€์ด ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. ํ‹€๋ฆฐ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“  ํŽธํ•˜๊ฒŒ ๋ง์”€ํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค! ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

๋ฐ˜์‘ํ˜•