LinkedList 구현하기
5 min read
자바의 LinkedList
클래스는 AbstractSequentialList
클래스를 상속하고
List
인터페이스와 Deque
인터페이스를 모두 구현한다.
이에 따라 리스트와 스택, 큐, 덱의 기능을 모두 제공할 수 있다.
각 요소가 데이터와 함께 이전 및 다음 요소에 대한 참조를 포함하는 노드로 구성 연결 리스트 기반으로 구현되어 있으며, 구체적으로는 양방향 연결 리스트(doubly linked list)가 기반이 되었다.
AbstractSequentialList
클래스는 AbstractList
클래스를 확장한 추상 클래스이다.
두 클래스 모두 List
인터페이스를 확장하고, 리스트를 구현하는데 필요한 일부 메서드를 제공해준다.
그러나 이 두 클래스는 데이터 접근 패턴에서 몇가지 차이점을 보인다.
AbstractSequentialList와 AbstractList의 차이점
- 접근 패턴 최적화
AbstractList
무작위 접근(Random Access)에 최적화AbstractSequentialList
는 순차적 접근에 최적화
- 필수 메소드 구현
AbstractList
를 상속받는 클래스는get(int index)
메소드 구현AbstractSequentialList
를 상속받는 클래스는listIterator(int index)
메소드 구현
특징
- 데이터 접근: 데이터에 순차적으로 접근(
O(n)
)하므로ArrayList
(O(1)
)에 비해 느리다. 그러나 시작 또는 끝은first
와last
로 저장되어있기 때문에O(1)
으로 접근 가능하다. - 양방향 연결: 각 요소(노드)는 이전 요소와 다음 요소에 대한 참조를 가지고 있어, 리스트를 양방향으로 순회가능하다.
- 동적 크기: 배열과 달리 데이터를 메모리 공간에 분산하여 할당하기 때문에 별도로 크기를 조정해줄 필요가 없다.
- 데이터 추가/삭제: 요소 자체의 추가나 제거는 노드의 참조만 변경하면 되기 때문에 빠르나, 해당 위치까지 리스트를 순회해야 하므로
O(n)
의 시간이 소요된다.
간단한 구현법
LinkedList
의 기능 역시 방대하다. 해당 아티클에서는 학습 목적 상, Deque
인터페이스와 Generic
, Iterator
, modCount
등 편의 및 성능을 위한 메서드들은 배제하였다.
추가적으로 싱글 연결 리스트로 변경하였다.
List 인터페이스 정의
아래 인터페이스는 실제 List 인터페이스에 정의되어 있는 메서드 중 일부를 발췌한 것이다.
이번에는 Generic
을 적용하지 않기 때문에, String 배열로 가정하고 진행하였다.
노드 클래스 정의
- 데이터를 담는
item
과 다음 노드를 참조하는next
로 구성.Double Linked List
의 경우, 이전 노드를 참조하는prev
추가
클래스 필드 및 생성자 정의
- first
- 리스트의 첫 번째 노드 (시작점)
- last
- 리스트의 마지막 노드 (끝점)
- 싱글 연결 리스트에서는 필요하지 않을 수 있으나,
- size
- 리스트에 저장된 요소의 총 갯수
- 생성자
- 기본 생성자로, 새로운
SimpleLinkedList
객체 초기화 - 명시적인 초기화 로직이 없으며, 필드들을 기본값으로 초기화
size
는 0,first
와last
는null
- 기본 생성자로, 새로운
순차적 접근 메서드 구현
private Node node(int index)
- 주어진
index
에 위치한 노드 반환 - 요소의 추가나 삭제 전, 공통적으로 요소 탐색이 발생하므로 private 메서드로 분리
- 주어진
add 구현
private void linkLast(String value)
- 리스트의 마지막에 새로운 노드 추가
- 새 노드(
newNode
)를 생성하고,last
를 이 새 노드로 업데이트 - 만약 리스트가 비어있으면(
tail == null
),first
도 새 노드로 설정 - 그렇지 않으면, 이전 마지막 노드(
tail
)의next
를 새 노드로 설정 - 리스트의 크기(
size
) 1 증가
private void linkBefore(String value, int index)
- 주어진
index
위치 바로 앞에 새로운 노드 삽입 index
가 0인 경우,first
를 새 노드로 업데이트- 그렇지 않은 경우,
index - 1
위치의 노드(prev
)를 찾고,prev.next
를 새 노드로 설정 - 새로운 노드는
prev.next
를next
로 업데이트 - 리스트의 크기(
size
) 1 증가
- 주어진
public boolean add(String value)
- 리스트의 끝에 새로운 요소 추가
- 항상
true
반환
public void add(int index, String value)
- 주어진
index
가 유효한 범위 내에 있는지 검증 index
가 리스트의 크기(size
)와 같은 경우,linkLast
메소드 호출- 그렇지 않은 경우,
linkBefore
메소드 호출 및 주어진index
위치에 새 요소 삽입
- 주어진
remove 구현
private String unlink(final Node prev, final Node node)
- 주어진 노드(
node
)를 리스트에서 제거하고, 그 노드의 값을 반환 prev
가null
인 경우,first
를next
로 업데이트- 그렇지 않은 경우,
prev.next
를next
로 설정하여,prev
와node
사이의 참조 제거 next
가null
인 경우,last
를prev
로 업데이트node
의item
과next
를null
로 설정, 참조 제거- 리스트의 크기(
size
) 1 감소 - 제거된 노드의 값을 반환
- 주어진 노드(
public boolean remove(String value)
- 주어진
value
와 일치하는 첫 번째 요소를 리스트에서 제거 - 리스트를 순회하면서 각 노드의 값을 주어진
value
와 비교 - 값이 일치하는 노드를 찾으면
unlink
메소드를 호출, 해당 노드 제거 및true
를 반환 - 일치하는 요소를 찾지 못한 경우,
false
반환
- 주어진
public String remove(int index)
- 주어진
index
에 위치한 요소를 리스트에서 제거하고, 그 요소의 값 반환 - 주어진
index
가 유효한 범위 내에 있는지 검증 index
가 0인 경우,unlink(null, first)
호출- 그렇지 않은 경우,
unlink(prev, prev.next)
를 호출하여index
위치의 노드 제거
- 주어진
get / set 구현
public String get(int index)
- 지정된
index
에 있는 요소 반환
- 지정된
public String set(int index, String value)
- 지정된
index
에 있는 요소를 새로운value
로 설정 후 이전 값 반환
- 지정된