Java List - 직접 구현해보기 5

직접 List 자료 구조를 다루면서 제네릭까지 변경하였다. 그러나 배열로 보관하는 elementData 에는 Object 타입을 사용하고 있다.

Object[] elementData 사용한 이유

  • 제네릭은 런타임으로 인한 이레이저 타입 정보가 사라지고, 런타임에서는 타입 정보가 필요하나, 생성자에서는 사용할 수 없다.
    • "new E[DEFAULT_CAPACITY];"
    • 제네릭 한계로 배열 생성 코드는 오작동하고, 컴파일 오류가 발생한다.
    • new 연산자와 같은 비슷한 한계 이유로 instanceof 키워드 또한 오동작한다.
  • 다른 방안으로 모든 데이터를 담는 Object[] 그대로 사용해야 했다.
    • new Object[DEFAULT_CAPACITY]

Object[] 사용해도 문제가 없는지 확인이 필요하다.


제네릭 타입 Object[]

제네릭 도입으로 문제가 없는지 살펴보자

private Object[] elementData;
public void add(E e) {
    // 코드 추가
    if (size == elementData.length) {
        grow();
    }

    elementData[size] = e;
    size++;
}

@SuppressWarnings("unchecked")
public E get(int index) {
    return (E) elementData[index];
}
  • 제네릭 타입 도입한 이후 add(E e) 메소드 입력 파라미터에는 런타임에 알아서 String, Integer 타입으로 변환할 것이다.
  • public E get(...) 메소드 반환에는 E 타입을 확인할 수 있고, return에는 다운캐스팅하도록 되어있다. 런타임에는 String, Integer 타입으로 변환되겠으나, 다운캐스팅으로 인한 문제를 발생하지 않을 것으로 보인다.

Object 타입은 모든 타입의 부모이므로 모든 데이터를 담을 수 있다. 조회에는 주의가 필요한데, 반환 타입(E->String)으로 다운캐스팅(E->String) 한다.

public E get(int index) {
    return (E) elementData[index];
}
  • elementData[index] 조회한 결과는 (E) Object 타입이다.
  • 배열의 (E) Object 데이터로 들어 간 것이 확실하므로 메소드 설계도 이를 고려하여 작성한다.
  • 메소드 E -> String인 입력으로 온 경우 (String) Object가 된다.
    • 메모리 기준으로 생각해보면 Object 타입을 찾아갔을 때 String 들을 감싸고 있다.
    • 만약 제네릭 도입하였는데, <E> 를 쓰지 않고 String, Integer 강제로 선언한 경우 get 메소드에서 예외 캐스트 오류가 발생할 가능성이 매우 높다.
    • 다운 캐스팅이 발생되지 않도록 타입들을 꼼꼼히 살펴봐야한다.

정리

  • CList 자류구조 보관하는 배열은 Object 타입으로 고정되어 있다.
  • 제네릭 도입 시 메소드의 입력과 반환 타입을 고정한다.
  • 제네릭으로 고정한 타입을 Object 배열로 보관한다.
  • 배열로 보관한 데이터를 제네릭 고정 타입으로 다운 캐스팅한다.


CList 단점

자류구조를 설계한 CList 단점은 다음과 같다.

  • 정확한 크기를 알지 못한 경우 메모리 낭비
    • 배열을 사용하고 뒷 부분은 사용되지 않으므로 낭비되는 메모리가 있음.
  • 데이터를 중간에 추가 및 삭제에는 비효율적임
    • 데이터를 한 칸씩 미는 것이 O(n) 성능을 가리키므로 성능이 좋지 못하다.
    • 데이터가 백만 건만 있어도 추가할 때마다 백만 건을 모두 밀어야 한다.