자바 리스트 인덱스 - jaba liseuteu indegseu

Java의 배열과 리스트에 대해 알아보겠습니다.

배열(Array)

여러 데이터를 하나의 이름으로 그룹핑하여 관리하기 위한 자료구조입니다. 논리적인 저장 순서와 물리적인 저장 순서가 같으며 연속된 메모리 공간을 차지합니다. 배열의 값에는 index를 통해 접근할 수 있습니다. 배열은 선언과 동시에 크기를 지정해야합니다.

int[] a = new int[5];       // 크기가 5인 int 타입 값을 연속으로 저장할 수 있는 배열 생성
int[] b = {1, 2, 3, 4, 5};  // 초기화를 통해 배열의 크기를 지정할수도 있습니다.

System.out.println(b[2]);   // 결과: 3 - 인덱스를 통해 값에 접근 가능합니다. 인덱스는 0부터 시작합니다.

장점

- 인덱스를 통해 검색이 용이합니다.

- 연속된 메모리 공간을 가지므로 관리에 용이합니다.

단점

- 크기가 고정되기 때문에 데이터가 삭제되는 경우 빈 공간이 생겨 메모리가 낭비됩니다.

- 배열의 크기를 컴파일 이전에 반드시 지정해야 하며, 초기에 크기가 지정되면 변경할 수 없습니다.

리스트(List)

순서가 있는 데이터들의 집합입니다. 불연속적인 메모리 공간에 데이터들이 연관되며 포인터를 통해 각 데이터들이 연결됩니다. 리스트는 동적으로 크기가 정해지며 데이터의 삽입, 삭제가 배열에 비해 용이하고 메모리의 재사용성이 높아집니다.

ArrayList

일반 배열과 ArrayList는 인덱스로 접근이 가능하다는 공통점이 있지만, ArrayList는 크기를 동적으로 늘릴 수 있다는 차이점이 있습니다.

ArrayList의 경우 초기 용량은 10이고, 초기에 지정한 용량(또는 기본 용량)을 초과할 경우 배열의 크기를 1.5배로 증가시킵니다.

List<Integer> listA = new ArrayList<>();     // 초기 용량은 10
List<Integer> listB = new ArrayList<>(100);  // 용량이 100인 리스트 생성

/**
리스트 초기화
**/
List<String> cities = Arrays.asList("Amsterdam", "Paris", "London");  // asList 로 초기화
// new 생성과 동시에 초기화
ArrayList<String> cities = new ArrayList<String>() {{
    add("Amsterdam");
    add("Paris");
    add("London");
}};
List<String> strings = List.of("foo", "bar", "baz");  // List.of 로 초기화
// stream으로 초기화
ArrayList<String> places = new ArrayList<>(Stream.of("Buenos Aires", "Córdoba", "La Plata").collect(Collectors.toList()));

배열과 ArrayList의 차이를 표로 살펴보겠습니다.

  배열 ArrayList
크기 정적(고정 크기) 동적
저장 데이터 타입 primitive type과 object 모두 저장 가능 object만 저장 가능
제네릭 사용 X O
길이 length 변수를 사용 size() 메소드를 사용
데이터 저장 할당 연산자(=)를 사용하여 저장 add() 메소드를 사용하여 저장

ArrayList에 데이터를 추가하고 삭제하는 방식을 그림으로 살펴보겠습니다.

자바 리스트 인덱스 - jaba liseuteu indegseu

ArrayList도 결국 배열이기 때문에 고정된 크기를 사용합니다. ArrayList의 크기(capacity)를 초과하여 데이터를 저장하는 경우 내부적으로 늘어난 크기의 새로운 배열을 생성하고(Arrays.copyOf()) 저장될 연속된 공간을 찾아 모든 값을 새롭게 저장합니다.

자바 리스트 인덱스 - jaba liseuteu indegseu

❗️크기(capacity) vs 길이(size)

capacity는 ArrayList가 수용할 수 있는 최대 크기이며, 초기에는 기본값이 10으로 정해집니다.

size는 실제 ArrayList 내에 저장된 데이터의 길이입니다.

만약 size가 capacity를 초과하는 경우에는 새로운 크기의 배열을 생성하여 기존 데이터들을 모두 복사한 뒤, 기존 capacity를 초과한 길이의 데이터들을 추가하는 작업이 일어납니다.

index를 통해 빠른 검색을 할 수 있지만, 데이터의 추가 및 삭제가 빈번하게 일어나는 경우 LinkedList를 사용하는 것이 더 효율적입니다.

LinkedList

노드 간에 연결(Link)을 통해 리스트를 구현한 것입니다. 인덱스를 가지고 있지 않고 다음 노드의 위치에 대한 포인터만 가지고 있기 때문에 순차 접근을 통해 데이터를 찾아야 합니다.

/**
  선언 및 초기화
**/
LinkedList list = new LinkedList();  //타입 미설정 Object로 선언
LinkedList<Integer> num = new LinkedList<Integer>();  //타입설정 int타입만 사용가능 (new에서 타입 파라미터 생략가능)
LinkedList<Integer> list2 = new LinkedList<Integer>(Arrays.asList(1,2, 3));  //생성시 값추가

/**
  데이터 추가 및 삭제
**/
num.addFirst(1); //가장 앞에 데이터 추가
num.addLast(2); //가장 뒤에 데이터 추가
num.add(3); //데이터 추가
num.add(1, 10); //index 1 위치에 데이터 10 추가

num.removeFirst(); //가장 앞의 데이터 제거
num.removeLast(); //가장 뒤의 데이터 제거
num.remove(); //생략시 0번째 index제거
num.remove(1); //index 1 제거
num.clear(); //모든 값 제거

/**
  데이터 출력
**/
System.out.println(list2.size()); // list 크기 : 3
System.out.println(list2.get(0)); // 0번째 index 출력
				
for(Integer i : list2) { // for문을 통한 전체출력
    System.out.println(i);
}

Iterator<Integer> iter = list2.iterator(); //Iterator 선언 
while(iter.hasNext()){//다음값이 있는지 체크
    System.out.println(iter.next()); //값 출력
}

/**
  값 검색
**/
System.out.println(list2.contains(1)); // list에 1이 있는지 검색 : true
System.out.println(list2.indexOf(1));  // 1이 있는 index반환 없으면 -1

LinkedList에 데이터를 추가하고 삭제하는 방법을 그림으로 살펴보겠습니다.

자바 리스트 인덱스 - jaba liseuteu indegseu

LinkedList를 직접 구현해보도록 하겠습니다. 직접 구현하는 LinkedList는 int 타입의 값을 저장할 수 있는 리스트입니다. 각 노드를 ListNode라는 클래스로 만들었습니다.

package study.linkedlist;

public class ListNode {
    int value;  // 실제 가지고 있는 값
    ListNode next;  // 다음 노드(의 주소)를 가리키는 포인터

    public ListNode(int value) {
        this.value = value;
        this.next = null;
    }
}

그런 다음 노드들의 관계를 관리하는 LinkedListManager 클래스를 만들어 보겠습니다. 이 클래스는 노드를 추가하고 삭제하는 등의 기능을 담당하고 있습니다.

package study.linkedlist;

public class LinkedListManager {
    public ListNode head = null;  // LinkedList의 가장 앞에 있는 노드
    private int size = 0;  // 연결된 노드의 수

    // 노드 리스트의 가장 마지막에 노드 추가
    public void add(ListNode nodeToAdd) {
        // LinkedList에 데이터가 없는 경우
        if (head == null) {
            head = nodeToAdd;  // 가장 앞 head에 데이터를 추가
            size++;  // 연결된 노드 수 증가
            return;
        }

        // LinkedList의 마지막 데이터를 찾아서
        ListNode tail = head;
        while (tail.next != null) {
            tail = tail.next;
        }

        // 마지막 데이터의 다음 노드로 추가하고 연결된 노드 수 증가
        tail.next = nodeToAdd;
        size++;
    }

    // 원하는 인덱스에 노드 추가
    public void add(ListNode nodeToAdd, int position) {
        // 인덱스가 음수이거나 size 보다 큰 경우는 에러!!!!!
        if (position < 0 || size - 1 < position) {
            // TODO: 에러 처리
            return;
        }

        // 맨 앞에 데이터를 추가하는 경우
        if (position == 0) {
            nodeToAdd.next = head;  // 원래 head가 가리키던 node를 추가할 노드의 다음 노드로 지정
            head = nodeToAdd;  // head가 지금 추가된 노드를 가리키도록 지정
        } else {
            // 추가하고자 하는 인덱스의 이전 노드를 구하고
            ListNode before = head;
            int cnt = 1;
            while (cnt < position) {
                before = before.next;
                cnt++;
            }

            nodeToAdd.next = before.next;  // 이전 노드가 가리키던 다음 노드를 현재 추가하는 노드의 다음 위치로 지정
            before.next = nodeToAdd;  // 이전 노드의 다음 위치를 현재 추가된 노드로 지정
        }

        size++;  // 새로운 노드가 추가되었으므로 연결된 노드 개수 증가
    }

    // 특정 인덱스의 데이터를 삭제
    public ListNode remove(int positionToRemove) {
        // 인덱스가 음수이거나 size 보다 큰 경우는 에러!!!!!
        if (positionToRemove < 0 || positionToRemove > size - 1) {
            // TODO: 에러 처리
            return null;
        }

        // 삭제해야 할 노드를 변수에 저장
        ListNode deleteNode = head;
        // 처음 데이터를 삭제하는 경우
        if (positionToRemove == 0) {
            head = head.next;  // head가 가리키던 node의 다음 node를 head가 가리키도록 변경
        } else {
            // 삭제하려고 하는 노드의 이전 노드를 찾아서
            ListNode before = head;
            int cnt = 1;
            while (cnt < positionToRemove) {
                before = before.next;
                cnt++;
            }

            deleteNode = before.next;  // 이전 노드가 가리키는 다음 노드를 삭제 노드 변수가 가리키도록 하고
            before.next = deleteNode.next;  // 삭제 노드가 가리키는 다음 노드를 이전 노드의 다음 노드로 지정
        }

        deleteNode.next = null;  // 삭제할 노드가 원래 가리키던 다음 노드에 대한 관계를 끊고
        size--;  // 노드가 삭제되므로 연결된 노드 수 감소
        return deleteNode;  // 삭제되는 노드 반환
    }

    // 해당 값을 포함하고 있는지 확인
    public boolean contains(int nodeToCheck) {
        ListNode findNode = head;
        // 현재 확인하는 노드가 null일 때까지 반복
        while (findNode != null) {
            // 현재 노드가 가진 값이 확인하는 값과 같은 경우 true
            if (findNode.value == nodeToCheck) {
                return true;
            }

            // 그렇지 않다면 다음 노드로 이동
            findNode = findNode.next;
        }

        // 원하는 값이 없는 경우 false
        return false;
    }

    // 연결되어 있는 노드의 수를 반환
    public int size() {
        return size;
    }

    // 연결된 노드들을 순서대로 출력
    public void printListNode() {
        ListNode node = head;
        while (node != null) {
            System.out.print(node.value + " ");
            node = node.next;
        }

        System.out.println();
    }
}

LinkedListManager를 생성하여 노드들을 추가, 삭제, 값을 포함하고 있는지를 확인해보겠습니다.

package study.linkedlist;

public class MakeLinkedList {
    public static void main(String[] args) {
        // 노드들을 LinkedList 방식으로 관리할 LinkedListManager 생성
        LinkedListManager linkedListManager = new LinkedListManager();

        // 1부터 5까지 값을 가진 노드를 차례대로 추가 (리스트의 가장 마지막에 추가)
        for (int i = 1; i <= 5; i++) {
            linkedListManager.add(new ListNode(i));
        }

        linkedListManager.printListNode();  // 출력: 1 2 3 4 5

        // int 값을 가진 노드를 원하는 인덱스에 추가
        linkedListManager.add(new ListNode(6), 2);
        linkedListManager.add(new ListNode(7), 0);

        linkedListManager.printListNode();  // 출력: 7 1 2 6 3 4 5

        // 해당 인덱스의 노드 삭제하기
        linkedListManager.remove(3);
        linkedListManager.remove(5);
        linkedListManager.remove(0);

        linkedListManager.printListNode();  // 출력: 1 2 3 4

        // 해당 값을 가진 노드가 있는지 확인
        System.out.println(linkedListManager.contains(2));  // 출력: true
        System.out.println(linkedListManager.contains(5));  // 출력: false
    }
}

참고

velog.io/@adam2/Array%EC%99%80-List%EA%B7%B8%EB%A6%AC%EA%B3%A0-Java-List

www.delftstack.com/ko/howto/java/initialize-arraylist-java/

velog.io/@dion/difference-between-array-and-list