JAVA/제네릭(Generic), 컬렉션(Collection Framework)

[컬렉션 프레임워크] 순회, 정렬, 전체 정리

jiyoon0000 2025. 4. 16. 04:01
순회
Iteration

* Iterable

1. Iterable이란?

  • 반복 가능한
  • 자바에서 Iterable은 반복 가능한 객체를 의미하는 인터페이스
  • List, Set 같은 컬렉션들은 모두 Iterable 인터페이스를 구현하고 있어, 향상된 for문(for-each) 문법에서 사용 가능

2. Iterable 인터페이스 구조

public interface Iterable<T> {
    Iterator<T> iterator();
}
  • iterator() 메서드로 Iterator 객체를 반환
  • iterator()를 통해 요소를 하나씩 꺼낼 수 있는 반복자(Iterator)를 생성

3. 향상된 for 문(for-each)

  • Iterable 인터페이스를 구현한 컬렉션이면 for-each문으로 순회 가능
  • 내부적으로 iterator()를 호출해 Iterator를 사용
for (String value : list) {
    System.out.println(value);
}

 

* Iterator

1. Iterator란?

  • 반복자
  • Iterator는 컬렉션 내부의 요소를 하나씩 꺼내기 위한 반복자 객체

2. Iterator 인터페이스 구조

public interface Iterator<E> {
    boolean hasNext(); 
    E next();          
    void remove();     
}
  • hasNext() : 다음 요소가 있는지 확인
  • next() : 다음 요소 반환
  • remove() : 현재 요소 삭제(optional)

+ remove() 메서드가 optional(선택적 기능)인 이유

  • Iterator는 기본적으로 순회(read-only) 기능에 집중되어 있음
  • 하지만, 어떤 상황에서는 순회하면서 요소를 삭제하는 경우도 있어 자바에서 remove() 메서드를 인터페이스에 정의해두었지만, 구현체가 반드시 구현하지 않아도 되는 선택사항으로 지정
  • remove()를 지원하는 컬렉션도 있고, UnsupportedOperationException 예외를 던지는 컬렉션도 있음
  • 주의
    • for-each에서는 remove() 사용 불가
      • for-each 문은 내부적으로 Iterator를 쓰지만, 우리가 직접 remove()를 호출할 수 없기 때문에 사용할 수 없음
      • 런타임 에러 가능성 -> ConcurrentModificationException
더보기
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
Iterator<String> iterator = list.iterator();

while (iterator.hasNext()) {
    String value = iterator.next();
    if (value.equals("b")) {
        iterator.remove(); // 안전하게 요소 제거
    }
}
System.out.println(list); // [a, c]
  • Iterator를 통해 remove()를 쓰면 ConcurrentModificationException 없이 안전하게 삭제 가능

 

3. Iterator가 필요한 이유

  • 내부 구현 방식과 무관하게 일관된 방식으로 요소를 순회할 수 있음
  • ex. ArrayList는 인덱스 기반, LinkedList는 포인터 기반
    • 둘의 내부 구조는 다르지만 Iterator로는 동일하게 순회 가능

* Iterable과 Iterator

  • Iterable : 컬렉션이 반복될 수 있도록 Iterator를 생성하는 역할
  • Iterator : 실제로 순회 로직을 수행하는 객체
  • 활용 : for-each, while(iterator.hasNext())
  • 구현 : ArrayList, HashSet, 사용자 정의 컬렉션 등
더보기
import java.util.Iterator;

public class MyArrayIterator implements Iterator<Integer> {

    private int currentIndex = -1;
    private int[] targetArr;

    public MyArrayIterator(int[] targetArr) {
        this.targetArr = targetArr;
    }

    @Override
    public boolean hasNext() {
        return currentIndex < targetArr.length - 1;
    }

    @Override
    public Integer next() {
        return targetArr[++currentIndex];
    }
}
import java.util.Iterator;

public class MyArray implements Iterable<Integer> {

    private int[] numbers;

    public MyArray(int[] numbers) {
        this.numbers = numbers;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new MyArrayIterator(numbers);
    }
}

* Iterator 반복자 패턴(Iterator Pattern)

1. 정의

  • 컬렉션 객체의 내부 구조를 노출하지 않고, 그 요소들에 순차적으로 접근할 수 있는 방법 제공
  • 컬렉션 내부 구현 방식(Array, LinkedList 등)과 무관하게 동일한 방식으로 데이터를 하나씩 처리할 수 있도록 만들어주는 패턴

2. 구성 요소

  • Iterator(반복자) : 실제로 요소를 하나씩 꺼내는 역할(hasNext, next 등)
  • Aggregate(집합 객체) : 반복자를 생성하는 객체(List, Set 등)
  • Client(사용자 코드) : 반복자를 사용해서 요소에 접근하는 코드(while(iterator.hasNext())

3. 필요한 이유

  • 컬렉션의 구현 방식과 무관한 순회 방식 제공
  • 캡슐화 유지 : 내부 구조를 외부에 노출하지 않음
  • 동일한 방식으로 순회 가능 -> 코드의 일관성 증가

4. 장점

  • 컬렉션 내부 구조 변경에 영향을 받지 않음
  • 다양한 컬렉션에 동일한 방식으로 접근 가능
  • 객체 지향 원칙 중 단일 책임 원칙(SRP), 개방-폐쇄 원칙(OCP)에 부합

정렬
Sorting

* 정렬이란?

  • 정렬 : 데이터를 일정한 기준(오름차순, 내림차순 등)에 따라 나열하는 작업
  • 기본 정렬 기준 :  오름차순(Comparable 기반)
  • 사용자 지정 기준 : Comparator

1. Comparable : 내부 정렬 기준

  • 자신이 정렬 기준을 정의
  • 자바에서 String, Integer, LocalDate 등 대부분 Comparable 구현

2. Comparator : 외부 정렬 기준

  • 객체 외부에서 정렬 기준을 정의할 수 있음
  • 하나의 객체에 대해 다양한 정렬 기준 적용 가능
  • ex. 나이순, 이름순
더보기
List<Person> list = new ArrayList<>();

// 이름 기준 오름차순 정렬
list.sort(new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
});

 

자바8 람다 사용

list.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));

+ Comparable 과 Comparator 우선순위

  • 둘 다 있는 경우 Collections.sort(list)는 Comparable 기준
  • Collections.sort(list, comparator) 또는 list.sort(comparator)를 사용하면 Comparator가 우선

* Comparable vs Comparator

항목 Comparable Comparator
정렬 기준 위치 객체 내부(자기 자신 기준) 객체 외부(별도 클래스 또는 람다)
인터페이스 선언 Comparable<T> Comparator<T>
메서드 이름 int compareTo(T o) int comapre(T o1, T o2)
정렬 기준 기본 정렬 기준 1개만 가능 여러 기준 정의 가능
코드 위치 정렬 대상 클래스 내부 외부에서 유연하게 구현 가능
사용 예 기본 정렬 기준이 명확할 때 -> 숫자, 이름 다양한 조건, 다중 정렬이 필요한 경우

 

* Arrays.sort()

  • 배열을 정렬할 수 있도록 도와주는 자바 표준 라이브러리 메서드
  • 기본 사용 : 오름차순 정렬
    • 기본 정렬은 오름차순이며 내부적으로 Dual-Pivot QuickSort(자바7 +) 사용
  • 객체 배열 정렬(Comparable)
    • 객체가 Comparable을 구현하고 있어야 함
    • 정렬 기준 : compareTo() 기준
  • 사용자 정의 정렬(Comparator)
    • 두 번째 매개변수에 Comparator를 전달해 정렬 기준을 외부에서 정의할 수 있음
    • 람다, 메서드 참조 활용 가능
  • 내부 정렬 알고리즘(Java 기준)
    • Primitive 배열
      • 알고리즘 : Dual-Pivot QuickSort
        • 2개의 피벗을 사용하여 배열을 3부분으로 나눠 각 구간에 대해 재귀적으로 정렬을 수행하여 빠름
      • 평균 O(n log n), 최악 O(n²), 빠름
    • 객체(Object) 배열
      • 알고리즘 : TimSort
        • 실제 데이터의 부분 정렬을 잘 활용하는 하이브리드 정렬
      • MergeSort + InsertionSort 혼합
      • 안정정렬
  • 예외 사항
    • 정렬 대상이 null 이면 NPE 발생
    • 정렬 대상이 Comparable을 구현하지 않았거나, Comparator가 없으면 ClassCastException 발생
      • 정렬 기준이 없으면 런타임에 예외 발생

* List 정렬 - Collections.sort()

  • 기본 정렬(Comparable 기반)
    • 요소가 comparable을 구현해야 함
    • 내부적으로 list.toArray() 후 배열 정렬 -> 다시 List에 반영
  • 사용자 정의 정렬(Comparator)
    • Collections.sort(list, Comparator.reverseOrder());
    • 내부 정렬은 TimSort
    • 객체 배열 정렬과 거의 동일하지만, List의 불변성/변형 가능성 고려 필요

* Tree 구조에서의 정렬(TreeSet/TreeMap)

  • TreeSet
    • 내부적으로 정렬된 이진 탐색 트리(레드-블랙 트리) 사용
    • 요소는 반드시 Comparable을 구현하거나 Comparator를 제공해야 함
  • TreeMap
    • Key는 자동으로 정렬됨(기본 오름차순)
    • Key가 Comparable을 구현해야 함

컬렉션 유틸

1. Collections 클래스

자바에서 java.util.Collections는 컬렉션(List, Set, Map 등)을 다룰 때 편리한 메서드를 모아놓은 유틸리티 클래스

 

* 주요 메서드

메서드 설명
sort(List<T> list) 리스트를 오름차순 정렬
sort(List<T> list, Comparator) 사용자 정의 정렬 기준으로 정렬
reverse(List<T> list) 리스트 요소 순서를 뒤집음
shuffle(List<T> list) 리스트 요소를 랜덤하게 섞음
swap(List<T> list, int i, int j) 두 인덱스의 값을 서로 바꿈
fill(List<T> list, T obj) 리스트 전체를 특정 값으로 채움
max(Collection<T>), min(Collection<T>) 최대값 / 최소값 반환
frequency(Collection<T>, T obj) 특정 요소가 등장한 횟수 반환
disjoint(Collection<?> c1, Collection<?> c2) 두 컬렉션이 겹치는 요소가 없는지 확인
unmodifiableList, unmodifiableSet 등 읽기 전용 컬렉션 생성
더보기
List<String> list = new ArrayList<>(List.of("banana", "apple", "cherry"));

Collections.sort(list); // 오름차순 정렬
Collections.reverse(list); // 역순 정렬
Collections.shuffle(list); // 무작위 섞기
System.out.println(list);

2. Arrays 클래스

java.util.Arrays는 배열을 다룰 때 유용한 유틸리티 클래스

 

* 주요 메서드

메서드 설명
sort(T[] array) 배열 오름차순 정렬
sort(T[] array, Comparator) 사용자 정의 기준으로 정렬
copyOf, copyOfRange 배열 복사
equals(arr1, arr2) 배열 간 요소 비교 (동일 여부)
fill(array, value) 배열 전체를 특정 값으로 채움
toString(array) 배열을 문자열로 출력
binarySearch(array, key) 정렬된 배열에서 이진 탐색
더보기
int[] nums = {5, 3, 2, 9, 1};
Arrays.sort(nums); // 오름차순 정렬
System.out.println(Arrays.toString(nums)); // [1, 2, 3, 5, 9]

3. 불변 컬렉션 vs 가변 컬렉션

  • 가변(Mutable) 컬렉션
    • 요소 추가, 삭제, 변경 가능
    • 일반적으로 사용하는 ArrayList, HashSet, HashMap 등
  • 불변(Immutable) 컬렉션
    • 요소 추가, 삭제, 수정 불가
    • 변경이 불가능하므로 안정성, 보안, 멀티스레드 안전성 확보에 유리
    • 생성 방법
      • List.of() : Java 9+ 지원, 불변 List 생성
      • Collections.unmodifiableList(list) : 기존 리스트를 읽기 전용으로 래핑
      • Set.copyOf(), Map.of() : Java 10+, 불변 컬렉션 생성
더보기
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ImmutableMain {

    public static void main(String[] args) {
        // 불변 리스트 생성
        List<Integer> list = List.of(1,2,3);

        // 가변 리스트
        ArrayList<Integer> mutableList = new ArrayList<>(list);
        mutableList.add(4);
        System.out.println("mutableList = " + mutableList);
        System.out.println("mutableList class = " + mutableList.getClass());

        // 다시 불변 리스트로
        List<Integer> unmodifiableList = Collections.unmodifiableList(mutableList);
        System.out.println("unmodifiableList class = " + unmodifiableList.getClass());
//        java.lang.UnsupportedOperationException
//        unmodifiableList.add(5);
    }

* Arrays.asList vs List.of

항목 Arrays.asList() List.of()
가변성 요소 변경 가능(add/remove 불가) 완전 불변
내부 구조 고정 크기 배열 기반 읽기 전용
null 허용 허용 허용X(NPE)
Java 버전 Java 1.2 이상 Java 9 이상
List<String> list1 = Arrays.asList("a", "b"); // 요소 변경 가능(크기 변경은 불가)
List<String> list2 = List.of("a", "b");       // 완전 불변

 

* 빈 컬렉션 생성

자바에서는 빈 컬렉션을 쉽게 생성할 수 있는 유틸 메서드 제공

List<String> emptyList = Collections.emptyList();
Set<String> emptySet = Collections.emptySet();
Map<String, String> emptyMap = Collections.emptyMap();
  • 반환되는 컬렉션은 불변 컬렉션
  • 크기가 0이기 때문에 읽기 전용으로만 사용 가능

* 멀티스레드 환경에서의 동기화 컬렉션

  • 문제점
    • 기본 컬렉션(ArrayList, HashMap 등)은 스레드가 안전하지 않음
    • 멀티스레드 환경에서 동시 접근 시 데이터 충돌 발생 가능
  • 해결 방법
    • Collections.synchronizedList(list) : 동기화된 List 생성
    • Collections.synchronizedMap(map) : 동기화된 Map 생성
    • CopyOnWriteArrayList, ConcurrentHashMap : 고성능 동기화 컬렉션(java.util.concurrent 패키지)
      • CopyOnWriteArrayList : 읽기 위주 작업이 많은 환경에 적합
      • ConcurrentHashMap : 성능과 스레드 안정성을 동시에 보장

컬렉션 프레임워크

1. 컬렉션 프레임워크

  • 자바에서 자료구조 + 알고리즘을 표준화해 제공하는 라이브러리 모음
  • 데이터의 저장, 탐색, 삭제 등 작업을 효율적으로 수행할 수 있도록 지원
  • 핵심 인터페이스 : List, Set, Map, Queue
분류 순서 중복 키-값 대표 구현체
List O O X ArrayList, LinkedList
Set X/O X X HashSet, LinkedHashSet, TreeSet
Map X Key X, Value O O HashMap, LinkedHashMap, TreeMap
Queue O O X LinkedList, ArrayDeque, PriorityQueue

2. Collection 인터페이스

  • 자바 컬렉션 프레임워크의 최상위 루트 인터페이스
  • List, Set, Queue 등 모든 컬렉션 타입의 공통 기능을 정의(공통 기능을 추상화한 설계도)
  • Map은 Collection 인터페이스를 상속받지 않고 별도로 존재

* 주요 메서드(Collection 공통)

메서드 설명
add(E e) 요소 추가
remove(Object o) 특정 요소 제거
contains(Object o) 포함 여부 확인
isEmpty() 비어 있는지 확인
size() 요소 개수 반환
clear() 전체 요소 제거
iterator() 반복자 반환
toArray() 배열로 변환

 

* 상속 구조

Collcetion은 실제 객체를 만들기 위한 것이 아니라, 공통 기능을 추상화하여 상위 설계를 가능하게 해주는 인터페이스

Collection
  ├── List
  │     └── ArrayList, LinkedList ...
  ├── Set
  │     └── HashSet, TreeSet ...
  └── Queue
        └── LinkedList, ArrayDeque ...

 

* Collection 인터페이스의 필요성

  1. 공통 기능의 통일된 설계 : 다양한 컬렉션(List, Set, Queue)이 갖는 공통 기능(add, remove 등)을 하나의 인터페이스로 정의해 일관된 사용 방식 제공
  2. 다형성 구현 가능 : Collection 타입으로 코드를 작성하면 실제 구현체가 ArrayList, HashSet 상관없이 유연하게 대체 가능
  3. 유지보수성 향상 : 구체 구현체에 의존하지 않으므로 나중에 자료구조를 바꾸더라도 코드 수정 최소화 가능
  4. 재사용성 증가 : 인터페이스를 기준으로 작성된 유틸성 메서드(Collections.copy())는 어떤 컬렉션에도 범용적으로 사용 가능

3. 주요 인터페이스별 특징

  • List
    • 순서 있음, 중복 허용
    • 인덱스로 요소 접근
    • 대표 구현체 : ArrayList, LinkedList
  • Set
    • 중복 허용 X, 순서 보장 X(구현체에 따라 다름)
    • 유일한 값만 저장
    • 대표 구현체
      • HashSet : 해시 기반, 순서 없음
      • LinkedHashSet : 순서 유지
      • TreeSet : 자동 정렬(이진 탐색 트리 기반)
  • Map
    • Key-Value 구조
    • Key : 중복 X, Value : 중복 O
    • 대표 구현체
      • HashMap : 가장 일반적, 순서 없음
      • LinkedHashMap : 입력 순서 유지
      • TreeMap : Key 기준 정렬
  • Queue/Deque
    • Queue : 선입선출(FIFO)
    • Deque : 양방향 큐(Queue + Stack 기능)
    • 대표 구현체 : LinkedList, ArrayDeque, PriorityQueue

4. 실무에서 컬렉션 선택 기준

  • 조회 중심 : ArrayList, HashMap
  • 빈번한 삽입/삭제 : LinkedList, LinkedHashSet
  • 중복 제거 : HashSet, TreeSet
  • 정렬된 데이터 : TreeSet, TreeMap
  • 키-값 저장 : HashMap, LinkedHashMap, TreeMap
  • 멀티스레드 환경 : ConcurrentHashMap, CopyOnWriteArrayList

총정리
  • 순회
    • Iterable : 컬렉션이 for-each 문에서 사용될 수 있게 해주는 인터페이스
    • Iterator : 컬렉션 내부 구조에 상관 없이 요소를 순차적으로 접근할 수 있도록 도와주는 객체
    • 자바 컬렉션은 모두 Iterable을 구현 -> 일관된 순회 가능
    • 사용자 정의 컬렉션도 Iterable + Iterator 구현 시 동일한 순회 방식 가능
  • 정렬
    • Comparable : 기본 정렬 기준을 클래스 안에서 직접 정의할 때
    • Comparator : 다양한 정렬 기준을 외부에서 정의해 유연하게 적용할 때
      • 실무에서는 정렬 기준이 계속 바뀌기 때문에 Comparator 사용이 더 일반적
  • 컬렉션 유틸 클래스
    • Collections : 정렬, 섞기, 역순, 최대/최소 등 유틸 메서드
    • Arrays : 배열 복사, 정렬, 탐색 유틸 메서드
    • 불변 컬렉션 : List.of(), Collections.unmodifiableList()
    • 동기화 컬렉션 : Collections.synchronizedList()
  • 컬렉션 선택 기준
    • 빠른 조회: ArrayList, HashMap
    • 삽입/삭제 많음: LinkedList, LinkedHashSet
    • 중복 제거: HashSet, TreeSet
    • 정렬 + 중복 제거: TreeSet, TreeMap
    • 멀티스레드 환경: ConcurrentHashMap, CopyOnWriteArrayList

<참고> - 김영한의 실전 자바 중급2편 강의 섹션 11. 컬렉션 프레임워크 순회, 정렬, 전체 정리

반응형