본문 바로가기
Java

자바 중급 2편 - List(1)

by KongJiHoon 2025. 8. 10.
  • List 자료구조
    • 순서가 있고, 중복을 허용하는 자료구조
    • 전에 배운 ArrayList와 LinkedList는 내부 구현만 다를 뿐 같은 기능을 제공한다. 하지만 내부구현이 다르기 때문에
      성능은 달라질 수 있다.

 

 

 

✅ MyList를 인터페이스로 구현해 MyArrayList와 MyLinkedList가 상속받게 구현

 

MyList 인터페이스

package com.example.collection.list;

public interface MyList<E> {

    int size();


    void add(int index, E e);

    E get(int index);

    E set(int index, E e);

    E remove(int index);

    int indexOf(E e);

}

 

MyArrayList 클래스

package com.example.collection.list;

import java.util.Arrays;

public class MyArrayList<E> implements MyList<E>{


    private static final int DEFAULT_CAPACITY = 5;

    private Object[] elementData;

    private int size = 0;

    public MyArrayList() {
        elementData = new Object[DEFAULT_CAPACITY];
    }

    public MyArrayList(int initialCapacity) {
        elementData = new Object[initialCapacity];
    }

    public int size() {
        return size;
    }



    public void add(E e) {

        // 코드 추가
        if (size == elementData.length) {

            grow();

        }

        elementData[size++] = e;

    }

    // 코드 추가
    @Override
    public void add(int index, E e) {
        if (size == elementData.length) {

            grow();

        }

        // 데이터 이동

        shiftRightFrom(index);

        elementData[index] = e;
        size++;
    }

    // 코드 추가, 요소의 마지막부터 index까지 오른쪽으로 밀기

    private void shiftRightFrom(int index) {

        for (int i = size; i > index ; i--) {
            elementData[i] = elementData[i - 1];
        }


    }



    // 코드 추가
    private void grow() {
        int oldCapacity = elementData.length;
        int newCapacity =oldCapacity * 2;

        // 배열을 새로만들고, 기존 배열을 새로운 배열에 복사


        elementData = Arrays.copyOf(elementData, newCapacity);
    }


    @SuppressWarnings("unchecked")
    @Override
    public E get(int index) {
        return (E) elementData[index];
    }

    public E set(int index, Object e) {

        E oldValue = get(index);

        elementData[index] = e;

        return oldValue;
    }

    // 코드 추가
    @Override
    public E remove(int index) {

        E oldValue = get(index);

        // 데이터 이동
        shiftLeftFrom(index);

        size--;

        elementData[size] = null;
        return oldValue;

    }

    // 코드 추가 요소의 index부터 마지막까지 왼쪽으로 밀기
    private void shiftLeftFrom(int index) {


        for (int i = index; i < size - 1; i++) {

            elementData[i] = elementData[i + 1];

        }


    }
    @Override
    public int indexOf(E o) {

        for (int i = 0; i < size; i++) {
            if (o.equals(elementData[i])) {
                return i;
            }
        }

        return -1;
    }

    public String toString() {

        return Arrays.toString(Arrays.copyOf(elementData, size)) + " size = " + size + ", capacity = " + elementData.length;

    }


}

 

MyLinkedList 클래스

 

package com.example.collection.list;

public class MyLinkedList<E> implements MyList<E> {

    private Node<E> first;
    private int size;


    public void add(E e) {
        Node<E> newNode = new Node<>(e);

        if (first == null) {
            first = newNode;
        } else {

            Node<E> lastNode = getLastNode();
            lastNode.next = newNode;

        }

        size++;

    }



    private Node<E> getLastNode() {

        Node<E> x = first;

        while (x.next != null) {
            x = x.next;
        }

        return x;
    }

    // 추가 코드
    @Override
    public void add(int index, E e) {

        Node<E> newNode = new Node<>(e);

        if (index == 0) {
            newNode.next = first;
            first = newNode;
        } else {

            Node<E> prev = getNode(index - 1);
            newNode.next = prev.next;
            prev.next = newNode;

        }
        size++;


    }
    @Override
    public E set(int index, E e) {

        Node<E> x = getNode(index);

        E oldValue = x.item;
        x.item = e;

        return oldValue;

    }


    // 추가 코드
    @Override
    public E remove(int index) {

        Node<E> removeNode = getNode(index);

        E removedItem = removeNode.item;

        if (index == 0) {
            first = removeNode.next;

        } else {

            Node<E> prev = getNode(index - 1);
            prev.next = removeNode.next;

        }


        removeNode.item = null;
        removeNode.next = null;
        size--;
        return removedItem;

    }

    private Node<E> getNode(int index) {

        Node<E> x = first;

        for (int i = 0; i < index; i++) {
            x = x.next;
        }

        return x;
    }
    @Override
    public E get(int index) {
        Node<E> node = getNode(index);
        return node.item;
    }
    @Override
    public int indexOf(E e) {

        int index = 0;
        for (Node<E> x = first; x != null; x = x.next) {

            if (x.item.equals(e)) {
                return index;
            }

            index++;

        }

        return -1;
    }
    @Override
    public int size() {
        return size;
    }





    @Override
    public String toString() {
        return "MyLinkedListV1{" +
                "first=" + first +
                ", size=" + size +
                '}';
    }


    private static class Node<E> {


        E item;
        Node<E> next;

        public Node(E item) {
            this.item = item;
        }
//
//    @Override
//    public String toString() {
//        return "Node{" +
//                "item=" + item +
//                ", next=" + next +
//                '}';
//    }


        // {A -> B -> C}
        @Override
        public String toString() {

            StringBuilder sb = new StringBuilder();

            Node<E> x = this;

            sb.append("[");
            while (x != null) {

                sb.append(x.item);
                if  (x.next != null) {
                    sb.append(" -> ");
                }
                x = x.next;
            }

            sb.append("]");

            return sb.toString();

        }
    }
}

 

✅ 리스트 추상화 - 의존관계 주입

  • IF) MyArrayList를 활용해서 많은 데이터를 처리하는 BatchProcessor 개발 중
  • 개발 완료 후 데이터를 앞에서 추가하는 상황이 많다고 가정
  • 이런 경우 MyLinkedList가 더욱 효율적이다
    • MyArrayList : O(n)
    • MyLinkedList : O(1)

 

예시 코드)

package com.example.collection.list;

public class BatchProcessor {


    private final MyList<Integer> list;

    public BatchProcessor(MyList<Integer> list) {
        this.list = list;
    }

    public void logic(int size) {

        long startTime = System.currentTimeMillis();

        for (int i = 0; i < size; i++) {

            list.add(0, i);

        }

        long endTime = System.currentTimeMillis();

        System.out.println("크기: " + size + ", 계산 시간: " +  (endTime - startTime));


    }
}

 

package com.example.collection.list;

public class BatchProcessorMain {


    public static void main(String[] args) {

        BatchProcessor batchProcessor1 = new BatchProcessor(new MyLinkedList<>());

        batchProcessor1.logic(1_000_000);

        BatchProcessor batchProcessor2 = new BatchProcessor(new MyArrayList<>());
        batchProcessor2.logic(50000);


    }
}

 

  • 어떤 MyList list의 구현체를 선택할지는 실행 시점에 생성자를 통해 결정
  • MyLinkedList, MyArrayList 인스턴스 둘 다 들어올 수 있다.
  • BatchProcessor의 외부에서 의존관계가 결정되어서 BatchProcessor인스턴스에 들어오는 것 같다.
  • 의존관계가 외부에서 주입되는 것 같다고 해서 의존관계 주입(Dependency Injection)이라 한다.

 

출처: 자바 중급 2편 - 김영한

'Java' 카테고리의 다른 글

자바 중급 2편 - List(3)  (3) 2025.08.10
자바 중급 2편 - List(2)  (1) 2025.08.10
자바 중급 2편 - LinkedList  (2) 2025.08.03
자바 중급 2편 - ArrayList(3)  (0) 2025.07.20
자바 중급 2편 - ArrayList(2)  (0) 2025.07.19