Java & Android

Java Collection - Vector, ArrayList, LinkedList의 비교

그레이트쪼 2016. 9. 3. 14:54

Collection

  • Collection interface는 "내부에 포함되는 요소는 순서를 가진다"라는 특징을 가지고 있다. (Collection에 속하는 애들은 List, Set, Queue등이다) 이와 반대로 포함요소가 순서에 관계없이 저장되는 녀석이 Map 계열이다. 

 


Vector

  • Vector는 Java 1.0때 만들어져 지금까지 유지되어온 클래스이다. (1.0 때에는 지금과 같은 List계열이 없었다) 기본적으로는 ArrayList와 동일하다. 
  • ArrayList와의 가장 큰 차이점은 synchronized 되었다는 차이이다. Vector는 thread safe를 위해서 내부 메소드들에 synchronized를 구현하였기 때문에 ArrayList에 비해 성능이 떨어진다. 
  • 요즘은 Vector를 거의 사용하지 않는데 만일 thread safe가 필요하다면 필요한 부분에만 직접 구현을 한다든지 아니면 SynchronizedList나 Map을 사용하는 것이 성능상 바람직하다. 
  • 사소한 차이로는 Vector는 내부 배열을 resize할 때 두배씩 커지는 데 비해 ArrayList는 50%씩 커진다.


ArrayList

  • ArrayList는 내부적인 자료구조로 배열(array)를 가지고 있다. 따라서 임의의 위치에 데이터를 추가하거나 삭제한다면 임시배열을 작성 후 데이터를 복사하는 방법을 사용한다. 

  • 따라서 잦은 추가 및 삭제, 특히 임의의 위치에 해당되는 원소에 대한 작업들은 성능 저하를 가져온다. 
  • 또 생성시점에 적절한 크기의 공간을 확보하지 않고 계속해서 원소를 추가하면 resize에 의한 성능 저하도 있다.
  • 대신 ArrayList는 임의의 원소에 대한 read가 빠르다. 따라서 많은 데이터를 한번에 가져와서 여러 번 access할 때에 적합한 자료구조이다. 
  • 아래는 실제 Java ArrayList 구현 코드 중 일부이다.  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class ArrayList<E> extends AbstractList<E>
           implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    ...
    // 'transient' means a variable should be used in memory
    // In most cases, this keyword is used for preventing serialize and deserialize
    // on object marshalling.
    private transient Object[] elementData;
    private int size;
 
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
 
    public ArrayList() {
        this(10);
    }
 
    // Increase the size of array 50% if it is too small to save data.
    // If it is still too small, make its size double and copy elements
    // (actullay elements' references) 
    public void ensureCapacity(int minCapacity) {
        modCount++;
        int oldCapacity = elementData.length;
        if (minCapacity > oldCapacity) {
            Object oldData[] = elementData;
            int newCapacity = (oldCapacity * 3)/2 + 1;
            if (newCapacity < minCapacity)
                newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }
 
    public int size() {
        return size;
    }
 
    public boolean isEmpty() {
        return size == 0;
    }
 
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
 
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
 
    @SuppressWarnings("unchecked"
    E elementData(int index) {
        return (E) elementData[index];
    }
 
    public E get(int index) {
        rangeCheck(index);
 
        return elementData(index);
    }
 
    public E set(int index, E element) {
        rangeCheck(index);
 
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
 
    public boolean add(E e) {
        ensureCapacity(size + 1);  // Increments modCount!!
        elementData[size++= e;
        return true;
    }
 
    public E remove(int index) {
        rangeCheck(index);
 
        modCount++;
        E oldValue = elementData(index);
 
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        elementData[--size] = null// Let gc do its work
 
        return oldValue;
    }
 
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
...

    • 라인 25의 ensureCapacity() 메소드를 보면 필요한 공간보다 배열의 크기가 작을 경우 resize하는 방식이 나온다. 일단 50% 증가를 한다음 그래도 작다면 100% 증가를 하는 방식이다. 그리고 나서 원소들을 모두 복사한다. 


LinkedList

  • LinkedList는 double linked된 list를 내부 자료구조로 사용한다. 따라서 임의의 위치에 있는 원소를 삽입, 삭제하는데 효과가 좋다. 
  • 다만 임의의 위치에 있는 데이터를 읽을 때 순차적으로 접근해야 하기 때문에 효과가 떨어진다. 
  • 물론 ArrayList에 비해 offer(), peek(), poll()와 같은 queue기반 operation들을 추가적으로 제공한다는 점도 차이점이긴 하다.

 

성능비교