Day 15: 컬렉션 List
Java Collections Framework는 데이터를 효율적으로 저장하고 관리하는 자료구조 라이브러리입니다. 배열과 달리 크기가 동적으로 변하며, 다양한 유틸리티 메서드를 제공합니다. 오늘은 가장 많이 사용되는 List 인터페이스를 집중적으로 다룹니다.
ArrayList 기본 사용법
배열 기반의 리스트로, 인덱스를 통한 빠른 접근이 장점입니다.
import java.util.ArrayList;
import java.util.List;
public class ArrayListBasic {
public static void main(String[] args) {
// ArrayList 생성
List<String> fruits = new ArrayList<>();
// 요소 추가
fruits.add("사과");
fruits.add("바나나");
fruits.add("포도");
fruits.add("딸기");
// 특정 위치에 추가
fruits.add(1, "오렌지");
// 요소 접근
System.out.println("첫 번째: " + fruits.get(0)); // 사과
System.out.println("크기: " + fruits.size()); // 5
System.out.println("포도 포함? " + fruits.contains("포도")); // true
System.out.println("포도 위치: " + fruits.indexOf("포도")); // 3
// 요소 수정
fruits.set(0, "청사과");
// 요소 삭제
fruits.remove("바나나"); // 값으로 삭제
fruits.remove(0); // 인덱스로 삭제
// 순회 방법들
// 1. for-each
for (String fruit : fruits) {
System.out.print(fruit + " ");
}
System.out.println();
// 2. forEach + 람다
fruits.forEach(fruit -> System.out.print(fruit + " "));
System.out.println();
// 비어있는지 확인
System.out.println("비어있나? " + fruits.isEmpty());
// 전체 삭제
fruits.clear();
System.out.println("삭제 후 크기: " + fruits.size());
}
}
ArrayList vs LinkedList
상황에 따라 적합한 리스트 구현체를 선택하는 방법을 알아봅니다.
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ListComparison {
public static void main(String[] args) {
// ArrayList: 내부적으로 배열 사용
// - 인덱스 접근: O(1) 빠름
// - 중간 삽입/삭제: O(n) 느림 (요소 이동 필요)
List<Integer> arrayList = new ArrayList<>();
// LinkedList: 내부적으로 이중 연결 리스트 사용
// - 인덱스 접근: O(n) 느림
// - 앞/뒤 삽입/삭제: O(1) 빠름
List<Integer> linkedList = new LinkedList<>();
// 성능 비교: 데이터 추가
long start;
// ArrayList: 끝에 추가
start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
arrayList.add(i);
}
System.out.println("ArrayList 끝에 추가: " +
(System.nanoTime() - start) / 1_000_000 + "ms");
// LinkedList: 끝에 추가
start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
linkedList.add(i);
}
System.out.println("LinkedList 끝에 추가: " +
(System.nanoTime() - start) / 1_000_000 + "ms");
// 인덱스 접근 비교
start = System.nanoTime();
for (int i = 0; i < 10000; i++) {
arrayList.get(50000);
}
System.out.println("ArrayList 인덱스 접근: " +
(System.nanoTime() - start) / 1_000_000 + "ms");
start = System.nanoTime();
for (int i = 0; i < 10000; i++) {
linkedList.get(50000);
}
System.out.println("LinkedList 인덱스 접근: " +
(System.nanoTime() - start) / 1_000_000 + "ms");
// 결론: 대부분의 경우 ArrayList를 사용
// LinkedList는 앞/뒤에서 빈번한 삽입/삭제가 있을 때만
}
}
List 정렬과 검색
Collections 유틸리티와 Comparator를 활용한 정렬입니다.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ListSortSearch {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>(List.of(38, 27, 43, 3, 9, 82, 10));
// 오름차순 정렬
Collections.sort(numbers);
System.out.println("오름차순: " + numbers);
// 내림차순 정렬
numbers.sort(Comparator.reverseOrder());
System.out.println("내림차순: " + numbers);
// 문자열 리스트 정렬
List<String> names = new ArrayList<>(List.of("홍길동", "김영희", "이철수", "박지민"));
// 사전순 정렬
Collections.sort(names);
System.out.println("사전순: " + names);
// 길이순 정렬
names.sort(Comparator.comparingInt(String::length));
System.out.println("길이순: " + names);
// 불변 리스트 생성
List<String> immutable = List.of("A", "B", "C");
// immutable.add("D"); // UnsupportedOperationException!
// 리스트 복사
List<String> copied = new ArrayList<>(immutable);
copied.add("D"); // 가능
// 유용한 메서드들
System.out.println("최대값: " + Collections.max(numbers));
System.out.println("최소값: " + Collections.min(numbers));
System.out.println("빈도: " + Collections.frequency(numbers, 10));
// 서브리스트
List<Integer> sub = numbers.subList(1, 4);
System.out.println("부분 리스트: " + sub);
}
}
실전 예제: 학생 관리 시스템
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
record Student(String name, int score, String grade) implements Comparable<Student> {
@Override
public int compareTo(Student other) {
return Integer.compare(other.score, this.score); // 점수 내림차순
}
}
public class StudentManager {
private final List<Student> students = new ArrayList<>();
void addStudent(String name, int score) {
String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> "B";
case 7 -> "C";
default -> score >= 60 ? "D" : "F";
};
students.add(new Student(name, score, grade));
}
void printAll() {
System.out.println("=== 전체 학생 목록 ===");
students.forEach(s ->
System.out.printf("%-6s %3d점 (%s)%n", s.name(), s.score(), s.grade()));
}
void printSorted() {
System.out.println("=== 성적순 ===");
students.stream()
.sorted()
.forEach(s -> System.out.printf("%-6s %3d점%n", s.name(), s.score()));
}
double getAverage() {
return students.stream()
.mapToInt(Student::score)
.average()
.orElse(0.0);
}
public static void main(String[] args) {
StudentManager manager = new StudentManager();
manager.addStudent("홍길동", 85);
manager.addStudent("김영희", 92);
manager.addStudent("이철수", 78);
manager.addStudent("박지민", 95);
manager.addStudent("최수진", 88);
manager.printAll();
manager.printSorted();
System.out.printf("전체 평균: %.1f점%n", manager.getAverage());
}
}
오늘의 연습문제
-
할일 목록 관리:
ArrayList를 사용한 ToDo 리스트를 만드세요. 추가, 삭제(인덱스), 완료 표시, 전체 목록 출력, 완료된 항목만 필터링 기능을 구현하세요. -
중복 제거: 정수 리스트에서 중복을 제거하면서 원래 순서를 유지하는 메서드를 작성하세요. (Set을 사용하지 않고 List만으로 구현)
-
학생 다중 정렬:
Student객체 리스트를 이름순, 점수순, 학점순으로 각각 정렬하는 Comparator를 만드세요. 점수가 같으면 이름순으로 정렬하는 복합 정렬도 구현하세요.