본문 바로가기

JAVA

[JAVA] ArrayList

Collection 인터페이스 구조

 


ArrayList란?

객체 배열을 구현한 클래스이며 컬렉션 인터페이스와 그 하위 List 인터페이스를 구현하였습니다.

객체 순서를 기반으로 순차적으로 자료를 관리하는 프로그램을 구현할 때 사용합니다.

 

ArrayList 생성자

ArrayList는 3가지 형태의 생성자가 있다.

    transient Object[] elementData;
    
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    // 초기 크기를 갖는 생성자
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                    initialCapacity);
        }
    }
    
    // 기본 생성자
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    // 주어진 컬렉션이 저장된 생성자
    public ArrayList(Collection<? extends E> c) {
    	...
    }
  • 기본생성자 사용 시 Object 타입의 객체 배열이지만 아직 초기화 하지 않은 상태에서 Object 타입의 객체 배열을 초기화 해주는 것을 볼 수 있습니다.
  • 초기 크기를 갖는 생성자는 파라미터로 넘어온 값 만큼 Object 객체 배열을 선언해주는 것을 확인할 수 있습니다.

여기서 ArrayList는 객체 배열을 구현한 클래스라는 것을 알 수 있습니다.

 

 

add메서드

    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    
    public void add(int index, E element) {
        rangeCheckForAdd(index);
        modCount++;
        final int s;
        Object[] elementData;
        if ((s = size) == (elementData = this.elementData).length)
            elementData = grow();
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        elementData[index] = element;
        size = s + 1;
    }

 

 

add 메서드에서는 선언한 객체 배열에 넘어온 실제 값을 배열에 넣어주고 size는 1추가 한것을 확인 할 수 있었습니다.

여기서 grow 메서드는 뭘까?

private Object[] grow(int minCapacity) {
    return elementData = Arrays.copyOf(elementData,
            newCapacity(minCapacity));
}

private Object[] grow() {
    return grow(size + 1);
}
private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity <= 0) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
}

grow메서드는 배열을 복사하여 리턴해주는 것을 확인할 수 있었습니다.

 

처음 ArrayList를 선언하고 add를 해주었을 때는 newCapacity메서드에서 Math.max(DEFAULT_CAPACITY, minCapacity)를 통해 10을 반환하는 것을 확인 할 수 있습니다. 

그렇기 때문에 ArrayList 선언 시 초기 크기를 지정해주지 않으면 기본 크기가 10이라는 것을 확인 할 수 있습니다.

 

Arrays.copy

add를 할 때 capacity를 넘기는 요청이 들어오면 grow 메서드에서 capacity를 더 늘린 배열을 복사하여 리턴을 해주는 방식입니다. 이것은 원래 데이터들을 모두 copy하여 집어 넣는 방식이기 때문에 애플리케이션 성능의 영향을 줄 수 있다고 생각합니다..

 

get메서드

E elementData(int index) {
    return (E) elementData[index];
}
    
public E get(int index) {
    Objects.checkIndex(index, size);
    return elementData(index);
}

 

get 메서드는 인덱스를 가지고 데이터를 접근합니다. (인덱스를 통한 Random Access가 가능합니다.)