본문 바로가기
Knowledge

LinkedList 직접 구현해 보기

by VENUSIM 2023. 7. 11.

요약 정리

LinkedList(연결 리스트)를 구현하기 앞서 해당 자료 구조에 대해 알아보자.

우선 List라고 하면 인덱스 구조로 위치를 갖고, 순서를 보장하는 자료구조라고 설명할 수 있겠다.

더 나아가 LinkedList는 삽입 / 삭제의 장점을 갖지만, 특정 노드 탐색시 다른 List에 비해 오래 걸린다는 단점이 있다.

 

삽입 / 삭제의 장점을 갖을 수 있는 이유는 LinkedList의 종류에 따라 다르지만, 모두 노드의 연결로 이루어져 있으며 인접 노드의 주소를 포인터로 가르키고 있어 중간에 삽입 / 삭제가 이루어 져도 해당 노드를 끊고 인접 노드의 주소를 연결해 주면 될뿐 대표적인 ArrayList처럼 데이터를 미루거나 당기는 작업이 필요 없다.

 

LinkedList에는 Singly, Doubly, Circular 세가지 종류가 있다.

Singly LinkedList : 단방향 연결 리스트로 다음 노드의 주소를 가르키는 구조이다.

Doubly LinkedList : 양방향 연결 리스트로 이전/다음 노드의 주소를 가르키는 구조이다.

Circular LinkedList : 원형 연결 리스트로 추가적으로 처음, 마지막 노드가 주소를 가르키고 있는 구조이다.

 

구현

가장 기본형인 Singly LinkedList 의 메서드를 구현했다. 
위에서 언급하였듯 다음 노드의 주소를 포인터로 가르키는 구조이기 때문에 next라는 필드로 다음 노드를 참조하도록 구성했다.

 

노드의 구성

- 현재 노드의 data를 저장할 data필드, 다음 노드를 참조할 next 필드를 정의하였다.

- 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하기 위해 Generic을 사용하였다.

- Node 객체 비교가 필요할 경우를 대비하여 equals와 hashCode를 재정의하였다.

- 추후 LinkedList에서 StringBuilder를 통해 객체의 모든 요소를 출력하기 위해 toString을 재정의하였다.

   public class Node<T> {
        private T data;
        private Node<T> next = null;

        public Node(T data) {
            this.data = data;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Node<?> node = (Node<?>) o;
            return Objects.equals(data, node.data) && Objects.equals(next, node.next);
        }

        @Override
        public int hashCode() {
            return Objects.hash(data, next);
        }

        @Override
        public String toString() {
            return data.toString();
        }
    }

 

가장 첫번째 노드를 저장할 Head 필드 구성

- static inner class로 노드를 사용 (Effective Java 3판 item24 참조)

    • static을 생략하면 바깥 인스턴스로의 숨은 외부 참조를 갖게 된다.
    • static을 생략하면 GC가 바깥 클래스의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수 있다.
public class SimpleLinkedList<T> {
    private Node<T> head = null;
    private static class Node<T> {
        ...
    }
}

 

SimpleLinkedList 구성

package Algorithms.자료구조.LinkedList;

import java.util.NoSuchElementException;
import java.util.Objects;

public class SimpleLinkedList<T> {

    private Node<T> head = null;
    
    private static class Node<T> {
    
        private T data;
        private Node<T> next = null;

        public Node(T data) {
            this.data = data;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Node<?> node = (Node<?>) o;
            return Objects.equals(data, node.data) && Objects.equals(next, node.next);
        }

        @Override
        public int hashCode() {
            return Objects.hash(data, next);
        }

        @Override
        public String toString() {
            return data.toString();
        }
    }

    // Node 추가 메서드
    public void add(T data) {
        // 첫번째 일 경우
        if(head == null) head = new Node<>(data);
        else {
            Node<T> node = this.head;
            while (node.next != null) node = node.next;
            // 마지막 노드를 탐색하여 인스턴스 생성
            node.next = new Node<>(data);
        }
    }

    // 노드 중간 삽입 메서드
    public void add(int index, T data) {
        if(index < 0) throw new IndexOutOfBoundsException("index can't smaller then 0");
        // 0번쨰 index라면 head 변경과 동일
        if(index == 0) {
            addFirst(data);
            return;
        }
        Node<T> temp = search(index);
        Node<T> node = new Node<>(data);
        node.next = temp.next;
        temp.next = node;
    }
	
    // 노드삭제
    public void remove(int index) {
        if(this.head == null) {
            if(0 < index) throw new IndexOutOfBoundsException("Index bigger then size");
            throw new NoSuchElementException("First Node is Empty");
        }
        // 첫번째 요소라면 헤드를 변경
        if(index == 0) {
            head = head.next;
            return;
        }
        int n = 1;
        Node<T> node = this.head;
        while (n < index) {
            if(node == null) {
                throw new IndexOutOfBoundsException("index bigger then size");
            }
            node = node.next;
            n++;
        }
        node.next = node.next == null ? null : node.next.next;
    }

    public void addFirst(T data) {
        if(this.head == null) throw new IndexOutOfBoundsException("index bigger then size");
        Node<T> node = new Node<>(data);
        node.next = this.head;
        this.head = node;
    }

    public void addLast(T data) {
        add(data);
    }

    public Integer indexOf(T data) {
        if(this.head == null) return null;
        int n = 0;
        Node<T> node = this.head;
        while (node != null) {
        	// 노드의 요소와 찾는 요소가 같다면 return
            if (node.data == data) return n;
            node = node.next;
            n++;
        }
        return null;
    }

    // 노드 순회
    private Node<T> search(int index) {
        if(this.head == null) throw new IndexOutOfBoundsException("index bigger then size");
        int n = 1;
        Node<T> node = this.head;
        while (n < index) {
            node = node.next;
            if (node == null) throw new IndexOutOfBoundsException("index bigger then size");
            n++;
        }
        return node;

    }

    @Override
    public String toString() {
        /*
        배열의 형식 구성
        ex) [1, 2, 3, ... , n]
        */
        StringBuilder stringBuilder = new StringBuilder("[");
        Node<T> node = this.head;
        stringBuilder.append(node);
        while (node.next != null) {
            //모든 노드 탐색
            node = node.next;
            stringBuilder.append(", ").append(node);
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }
}

 

Main 메서드를 이용한 메서드 테스트

package Algorithms.자료구조.LinkedList;

public class Main {
    public static void main(String[] args) {

        SimpleLinkedList<Integer> simpleLinkedList = new SimpleLinkedList<>();
        simpleLinkedList.add(4);
        simpleLinkedList.add(0,2);
        simpleLinkedList.add(1,3);
        simpleLinkedList.add(0,3);
        System.out.println(simpleLinkedList);
        simpleLinkedList.addFirst(1);
        simpleLinkedList.addLast(5);
        System.out.println(simpleLinkedList);

        simpleLinkedList.remove(simpleLinkedList.indexOf(3));
        System.out.println(simpleLinkedList);
    }
}

 

 

간단한 동작 구현이라 예외 처리가 부족한 부분이 있을 수 있습니다. 긴 글 읽어 주셔서 감사합니다 :)

'Knowledge' 카테고리의 다른 글

[JVM] Java 메모리 관리  (2) 2024.12.27
Trie 자료구조 - 14425  (1) 2023.07.19
SQL  (1) 2023.01.09
JDBC  (0) 2023.01.09
Servlet & JSP  (0) 2023.01.09

댓글