순회
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
- for-each에서는 remove() 사용 불가
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²), 빠름
- 알고리즘 : Dual-Pivot QuickSort
- 객체(Object) 배열
- 알고리즘 : TimSort
- 실제 데이터의 부분 정렬을 잘 활용하는 하이브리드 정렬
- MergeSort + InsertionSort 혼합
- 안정정렬
- 알고리즘 : TimSort
- Primitive 배열
- 예외 사항
- 정렬 대상이 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 인터페이스의 필요성
- 공통 기능의 통일된 설계 : 다양한 컬렉션(List, Set, Queue)이 갖는 공통 기능(add, remove 등)을 하나의 인터페이스로 정의해 일관된 사용 방식 제공
- 다형성 구현 가능 : Collection 타입으로 코드를 작성하면 실제 구현체가 ArrayList, HashSet 상관없이 유연하게 대체 가능
- 유지보수성 향상 : 구체 구현체에 의존하지 않으므로 나중에 자료구조를 바꾸더라도 코드 수정 최소화 가능
- 재사용성 증가 : 인터페이스를 기준으로 작성된 유틸성 메서드(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. 컬렉션 프레임워크 순회, 정렬, 전체 정리
'JAVA > 제네릭(Generic), 컬렉션(Collection Framework)' 카테고리의 다른 글
[컬렉션 프레임워크] Map, Stack, Queue, Deque (0) | 2025.04.14 |
---|---|
[컬렉션 프레임워크] Set (0) | 2025.04.10 |
[컬렉션 프레임워크] HashSet (0) | 2025.04.08 |
[컬렉션 프레임워크] Hash (0) | 2025.04.07 |
[컬렉션 프레임워크] List (0) | 2025.04.04 |