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) 성능을 가리키므로 성능이 좋지 못하다.
- 데이터가 백만 건만 있어도 추가할 때마다 백만 건을 모두 밀어야 한다.