CodeGym /행동 /JAVA 25 SELF /List: ArrayList와 LinkedList, 기본 연산

List: ArrayList와 LinkedList, 기본 연산

JAVA 25 SELF
레벨 26, 레슨 1
사용 가능

1. 클래스 ArrayList

Java에서 인터페이스 List는 다음과 같은 계약입니다: “저는 정렬된 요소 컬렉션이며, 인덱스로 요소를 가져올 수 있습니다”.

리스트(List)의 핵심 특성:

  • 요소가 특정 순서로 저장됩니다(집합과 달리).
  • 중복 요소를 저장할 수 있습니다(예: 두 번 "Vasya").
  • 인덱스로 요소에 접근할 수 있습니다: 첫 번째 요소의 인덱스는 0, 두 번째는 1 등입니다.
  • 리스트의 어느 위치에나 요소를 추가하고 제거할 수 있습니다.

인터페이스 List의 가장 인기 있는 구현은 ArrayList입니다. 내부적으로 일반 배열을 사용하며, 데이터가 늘어나면 자동으로 확장됩니다.

ArrayList 생성 방법

import java.util.ArrayList;
import java.util.List;

public class Example {
    public static void main(String[] args) {
        // 문자열 리스트를 생성합니다
        List<String> students = new ArrayList<>();
    }
}

설명

  • List<String>는 “문자열 리스트” 타입의 변수입니다. 변수는 인터페이스로 선언하고, 객체는 구체 클래스(ArrayList)로 생성합니다.
  • 꺾쇠 괄호 <String>는 리스트 요소의 타입을 지정합니다(제네릭, generics).

ArrayList(및 모든 List)의 기본 메서드:

  • add(element) — 요소를 리스트 끝에 추가합니다.
  • add(index, element) — 지정된 인덱스에 요소를 삽입합니다.
  • get(index) — 인덱스로 요소를 가져옵니다.
  • set(index, element) — 인덱스 위치의 요소를 교체합니다.
  • remove(index) — 인덱스로 요소를 삭제합니다.
  • remove(Object) — 전달된 객체와 같은 첫 번째 요소를 삭제합니다.
  • size() — 리스트의 요소 개수를 확인합니다.

예시: 학생 목록 다루기

import java.util.ArrayList;
import java.util.List;

public class StudentListDemo {
    public static void main(String[] args) {
        List<String> students = new ArrayList<>();

        // 학생 추가
        students.add("Vasya");
        students.add("Petya");
        students.add("Masha");

        // 모든 학생 출력
        System.out.println("학생 목록: " + students);

        // 첫 번째 학생 가져오기
        String first = students.get(0);
        System.out.println("첫 번째 학생: " + first);

        // 두 번째 학생의 이름 변경
        students.set(1, "Pavel");
        System.out.println("변경 후: " + students);

        // Masha 삭제
        students.remove("Masha");
        System.out.println("Masha 삭제 후: " + students);

        // 리스트 크기
        System.out.println("총 학생 수: " + students.size());
    }
}

결과:

학생 목록: [Vasya, Petya, Masha]
첫 번째 학생: Vasya
변경 후: [Vasya, Pavel, Masha]
Masha 삭제 후: [Vasya, Pavel]
총 학생 수: 2

우리 애플리케이션과 어떻게 연결될까요?
학생 과제를 관리하는 애플리케이션이 있다고 가정해 봅시다. 이제 배열 대신 List<String>로 과제 목록을 저장하고, 동적으로 새로운 과제를 추가할 수 있습니다.

2. 클래스 LinkedList: 삽입/삭제 속도가 중요할 때

LinkedList는 리스트의 또 다른 구현입니다. 이중 연결된 체인으로 구성되어 각 노드는 이전과 다음 요소를 알고 있습니다. 마치 열차 편성처럼, 중간에 “객차”를 넣어도 전체를 재배열하지 않고 빠르게 처리할 수 있습니다.

LinkedList 생성

import java.util.LinkedList;
import java.util.List;

public class Example {
    public static void main(String[] args) {
        List<String> tasks = new LinkedList<>();
    }
}

LinkedList의 특징

  • 리스트의 시작과 중간에서 삽입과 삭제가 빠릅니다.
  • 인덱스로 접근하는 것은 느립니다(100번째 요소를 찾으려면 체인을 따라가야 함).
  • 끝뿐 아니라 시작이나 중간에도 자주 요소를 추가/삭제한다면 적합합니다.

예시: LinkedList 사용

import java.util.LinkedList;
import java.util.List;

public class TaskListDemo {
    public static void main(String[] args) {
        List<String> tasks = new LinkedList<>();

        tasks.add("일어나기");
        tasks.add("아침 먹기");
        tasks.add("수업 들으러 가기");

        // 작업을 맨 앞에 추가
        tasks.add(0, "알람 설정하기");

        System.out.println("작업 목록: " + tasks);

        // 첫 번째 작업(가장 이른 작업) 삭제
        tasks.remove(0);

        System.out.println("첫 번째 작업 삭제 후: " + tasks);
    }
}

3. ArrayList와 LinkedList 비교

기준
ArrayList
LinkedList
기반 배열 이중 연결 리스트
인덱스로 빠른 접근 예 (O(1)) 아니요 (O(n))
시작/중간에서 빠른 삽입/삭제 아니요 (O(n)) 예 (O(1) — 참조를 알고 있는 경우)
끝에서 빠른 삽입/삭제 예 (보통 O(1)) 예 (O(1))
메모리 메모리 사용량이 적음 더 많음(이웃 노드에 대한 추가 참조)
일반적인 용도 인덱스 기반 접근이 빈번함 삽입/삭제가 빈번함

간단한 규칙:

  • 인덱스로 빠르게 접근해야 한다면 ArrayList를 사용하세요.
  • 시작이나 중간에 자주 추가/삭제한다면 LinkedList를 사용하세요.

4. 리스트에서 자주 쓰는 연산

리스트 순회

기본 for 루프

for (int i = 0; i < students.size(); i++) {
    System.out.println("학생 #" + i + ": " + students.get(i));
}

for-each 루프(가장 흔한 방법)

for (String name : students) {
    System.out.println("이름: " + name);
}

람다 표현식(Java 8+)

students.forEach(name -> System.out.println("이름: " + name));

요소 검색

  • contains(element) — 요소가 리스트에 있으면 true를 반환합니다.
  • indexOf(element) — 요소가 처음 나타나는 인덱스를 반환하고, 없으면 -1을 반환합니다.
if (students.contains("Vasya")) {
    System.out.println("Vasya가 목록에 있습니다!");
}
int index = students.indexOf("Vasya");
System.out.println("Vasya의 인덱스: " + index);

리스트 비우기

clear() — 모든 요소를 삭제합니다.

students.clear();
System.out.println("비운 후 목록: " + students);

5. ArrayList는 언제 쓰고, LinkedList는 언제 쓸까?

다음처럼 생각하는 게 쉽습니다: ArrayList는 인덱스로 요소에 빠르게 접근해야 하고, 리스트가 자주 변경되지 않을 때 좋습니다. 예를 들어, 긴 사용자 목록이나 메시지 기록처럼 대부분 읽기 위주인 경우입니다.

반대로 LinkedList는 시작/중간에 요소를 자주 삽입하거나 삭제할 때 유용합니다. 큐, 스택, 실행 취소 기록 등의 사례가 이에 해당합니다.

실무에서는 대부분 ArrayList를 사용합니다. LinkedList는 “비상용 도구”처럼 서랍에 넣어두었다가, 가끔 꼭 필요한 상황에서 쓰게 됩니다.

6. 리스트 작업 시 흔한 실수

실수 №1: 리스트 범위를 벗어남. 가장 흔한 상황은 존재하지 않는 인덱스로 접근하는 것입니다. 리스트에 요소가 세 개뿐인데 students.get(5)를 호출하면 IndexOutOfBoundsException이 발생합니다. 접근 전에 size()를 확인하세요.

실수 №2: 순회 중 요소 삭제. for-each로 순회하면서 동시에 요소를 삭제하면 ConcurrentModificationException이 발생합니다. 복잡한 삭제가 필요하면 인덱스 기반 루프나 Iterator를 사용하세요.

실수 №3: 객체 비교를 잘못함. 사용자 정의 객체(예: Student)를 저장하는 경우, contains와 remove는 equals에 의존합니다. 이를 재정의하지 않으면 내용이 아니라 참조로 비교합니다.

실수 №4: raw 타입 사용. List list = new ArrayList()처럼 작성하지 말고, 항상 요소 타입을 명시하세요: List<String> list = new ArrayList<>(). 제네릭은 오류를 방지하고 코드를 더 이해하기 쉽게 해줍니다.

1
과제
JAVA 25 SELF, 레벨 26, 레슨 1
잠금
학생 시간표 🗓️✍️
학생 시간표 🗓️✍️
1
과제
JAVA 25 SELF, 레벨 26, 레슨 1
잠금
내 개인 영화 목록 "볼 영화" 🎬🍿
내 개인 영화 목록 "볼 영화" 🎬🍿
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION