List (ArrayList, LinkedList)
List (ArrayList, LinkedList)
- 저장된 요소들의 순서가 있고 데이터에 중복이 가능하다.
- 자바에서는 리스트를 ArrayList와 LinkedList 두가지 기본 형태로 나타낸다.
List 구현체
1. ArrayList
- Resizable한 List 인터페이스의 구현체이다.
- 배열의 최대 크기만큼 원소를 추가할 수 있고 이 배열이 차면 더 큰 배열을 새로 할당한 다음 기존 값을 복사한다.
- ArrayList는 처음에 빈 배열로 시작하고 처음 원소가 추가될 때 용량 10 기반 배열을 할당한다.
- 초기 용량값을 생성자에 전달하면 하지않아도 된다.
1-1 ArrayList 주요 필드
1
2
3
transient Object[] elementData;
...
private int size;
- elementData
- 값을 담고있는 Object 배열이다
- size
- ArrayList의 크기이다.
1-2 ArrayList 생성자
1
2
3
4
5
6
7
8
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // 0
}
public ArrayList(int initialCapacity) {
...
this.elementData = new Object[initialCapacity];
}
- 파라미터가 없는 생성자로 ArrayList를 선언하게 되면 빈 Object 배열 객체가 elementData에 할당이 된다.
- 인스턴스를 생성할 때 생성자에 int형 파라미터를 넘기게 되면 값에 따라 초기화된다.
1-3 ArrayList add 메소드
- 초기화된 ArrayList에 add 메소드를 호출하면 내부에서 어떤 동작이 발생하는지 살펴보자.
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
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
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) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY,minCapacity)];
}
}
- add가 호출되면 private으로 오버로딩 된 add를 호출한다.
- 넣을 element와 가지고 있는 element 그리고 size를 넘겨준다.
- 현재 size가 배열의 길이와 같다면 grow 메소드를 호출하고 그렇지않다면 해당 인덱스에 값을 저장 후 사이즈를 늘린다.
- grow 메소드가 호출되면 현재 size + 1을 파라미터로 넘겨준다.
- 이후 Arrays.copyOf 메소드에 현재 배열과 새로운 길이를 넘겨 길이가 늘어난 복사된 배열을 리턴한다.
ensureCapacity() 메서드를 이용해 ArrayList 용량을 늘려도 크기 조정 작업을 건너 뛸 수 있다.
2. LinkedList
- 노드로 이루어진 선형의 자료구조이다.
2-1 LinkedList 주요 필드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
transient int size = 0;
...
transient Node<E> first;
transient Node<E> last;
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
- Node
- Node가 데이터를 담고 있으며, 이전 Node와 다음 Node에 대한 정보도 가지고 있다.
- Node를 변경함으로써 삽입과 삭제를 하며 재정렬이 필요없기 때문에 ArrayList에 비해 상대적으로 삽입과 삭제가 빠르다
2-3 LinkedList add 메소드
초기화된 LinkedList에 add 메소드를 호출하면 내부에서 어떤 동작이 발생하는지 살펴보자.
- 첫번째 노드에 삽입 시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public void addFirst(E e) { linkFirst(e); } private void linkFirst(E e) { final Node<E> f = first; final Node<E> newNode = new Node<>(null, e, f); first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; }
- 새로운 노드를 생성하면서 next에 기존의 첫번째 노드를, 기존 첫번째 노드는 prev는 생성된 노드를 바라보게 한다.
마지막 노드에 삽입 시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public boolean add(E e) { linkLast(e); return true; } void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
- 새로운 노드를 생성하면서 prev는 last Node, next는 null로 생성한다.
- 기존의 last Node의 next는 새로 생성한 node를 바라보게 한다. 그리고 last를 새로만든 Node로 만든다.
ArrayList vs LinkedList
- 둘중 어느 것을 쓸지 아니면 비표준 List 구현체를 쓸지는 데이터 접근/수정 패턴에 따라 다르다.
- ArrayList의 특정 인덱스에 원소를 추가하려면 다른 원소들을 모두 한 칸씩 우측으로 이동시켜야한다.
- LinkedList는 삽입 지접을 찾기 위해 노드 레퍼런스를 죽 따라가는 수고는 있지만 삽입 작업은 노드를 하나 생성한 다음 두 레퍼런스를 세팅하면 끝난다.
This post is licensed under CC BY 4.0 by the author.