우테코 프리코스 기간 중 배열을 사용할까? 컬렉션을 사용할까? 에 대한 고민이 있었고 많은 코드 리뷰에서도 컬렉션을 사용하는 사람들이 많았다. 개인적으로 배열을 사용하는 것이 올바르다고 생각했는데 그것이 과연 맞을까? 근거를 명확하게 말할 수 있을까 싶어 글을 작성하게 되었다.
목차
1. 배열이란?
2. 컬렉션이란?
3. 배열 vs 컬렉션
4. 결론
java에서 배열이란 기존 변수의 단점을 상쇄하여 연속된 값을 저장하도록 하는 자료구조이다. 특이점으로는 길이 조절이 불가하다. 지정된 길이 값으로 메모리를 미리 할당한다.
길이 조절을 하고싶다면 새로운 배열을 할당하여 넣어주는 방법 뿐이다.
배열은 객체로 heap 메모리에 저장된다.
컬렉션이란 기존 배열 개념을 향상시켜 데이터 처리에 용이하도록 만든 인터페이스다.
배열은 길이를 한 번 지정하면 늘릴 수 없고 여러가지 처리에 너무 불편하게 설계되어있다. 더욱 향상된 프로그래밍을 위해서는 컬렉션을 사용해야한다.
1, 2를 봤을 때 컬렉션의 장점만 매우 두드러지게 나온다.
하지만 구현하려는 시스템의 목적과 데이터 처리 flow를 확인했을 때 컬렉션이 일종의 기술 부채로 남게 될 수도 있다. (이건 내 생각)
먼저, 배열과 컬렉션의 성능을 비교해보도록 하자.
private void getArrayTime(){
Time time=new Time();
time.setStartTime();
int idxNumber=array[9];
time.setEndTime();
time.printNanoSec();
}
private void getListTime(){
Time time=new Time();
time.setStartTime();
int idxNumber=list.get(9);
time.setEndTime();
time.printNanoSec();
}
👉🏻 결과는?
[Array]
666ns
[List]
3166ns
위와 같은 결과가 도출된다. ns라 매우 짧은 '순간' 이지만, 5배가 차이난다. (사실 무시 가능한 정도이긴 함)
이건 ArrayList 구현체를 보면 쉽게 알 수 있다.
ArrayList 구현체는 사실 배열의 Wrapper class라고 할 수 있다.
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
transient Object[] elementData;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // 빈 Object 배열임
}
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
}
간단하게 구현 클래스를 좀 뜯어오면 위와 같이 확인할 수 있다.
get() 메소드에서 내부적으로 몇개의 연산을 거치기 때문이다.
만약에 ArrayList를 안 쓰고 LinkedList를 쓴다고 하더라도 index까지 연결된 데이터를 타고 -> 타고 -> 들어가야 하기 때문에 결국 o(index)의 시간 복잡도를 갖게 된다.
컬렉션은 다양한 기능이 매우 많고 시간적으로 큰 차이가 나는 것이 아니기 때문이다.
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
배열에 할당된 사이즈를 넘어 데이터를 추가하고 싶다면? 배열을 복사하는 과정을 코드로 작성해야 하기 때문에 번거로울 것이다. 하지만 컬렉션에는 이와 같은 기능이 다 구현되어 있다. 위 코드가 방금 설명한 그것이다.
데이터를 조작해야 할 때 에는 무조건 컬렉션을 사용하는 것이 맞다고 생각한다. 인터페이스의 안정성이 20% 귀찮음이 80%의 이유이다.
하지만 굳이 데이터를 조작하지 않거나 간단한 로직에서는 배열을 사용하는 것이 맞다는 것이 개인적인 의견이다.
사실 이전까지는 크게 공부 안 하고 기능을 '습득'해서 대충 사용하곤 했는데 다시 돌이켜보면 모르면 그냥 안 쓰는 게 낫다고 생각하게 된다. 공부를 하고 쓰든가 잘 모르면 그냥 쓰지를 말든가.
<참고 문서>
https://docs.oracle.com/javase/tutorial/collections/interfaces/index.html
https://stackoverflow.com/questions/19389609/array-vs-arraylist-in-performance