
알고리즘 문제를 풀다보면 대부분 처음 고민하게 되는 것은 어떻게 데이터를 받아오고, 정렬하할 것이다.
자바에서 데이터를 어떻게 저장하고, 어떻게 꺼내고, 어떻게 정렬할 것인가'는 거의 항상 컬렉션 프레임워크로 귀결된다.

위의 표처럼 다양한 컬렉션 프레임 워크가 있다. 오늘은 그중에서 가장 근간이 되는
List 계열, Set 계열, Map 계열에 대해서 알아보고자 한다.
ArrayListLinkedListVector1) 추가(Add)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
void | add(int index, E element) | 지정한 인덱스에 요소 삽입(뒤 요소는 한 칸씩 밀림) |
boolean | add(E e) | 리스트 끝에 요소 추가(성공 여부 반환) |
boolean | addAll(int index, Collection<? extends E> c) | 지정한 인덱스에 컬렉션의 모든 요소를 삽입 |
boolean | addAll(Collection<? extends E> c) | 리스트 끝에 컬렉션의 모든 요소를 추가 |
2) 삭제/비우기(Remove/Clear)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
void | clear() | 리스트의 모든 요소 삭제(비움) |
3) 포함 여부(Contains)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | contains(Object o) | 특정 요소가 포함되어 있는지 확인 |
boolean | containsAll(Collection<?> c) | 컬렉션 c의 모든 요소가 리스트에 포함되어 있는지 확인 |
4) 주요 메서드: 복사(Copy)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
static <E> List<E> | copyOf(Collection<? extends E> coll) | 주어진 컬렉션을 복사해 새 리스트를 반환(원본과 별개) |
5) 비교 / 동등성(Equals / Hash)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | equals(Object o) | 리스트 내용이 같은지 비교(순서 포함) |
int | hashCode() | 리스트의 해시값 반환(동등성 규약과 연계) |
6) 조회(Get)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
E | get(int index) | 해당 인덱스의 요소 반환 |
7) 검색(Search)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
int | indexOf(Object o) | 요소 o의 첫 등장 인덱스 반환(없으면 -1) |
8) 상태 확인
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | isEmpty() | 리스트가 비어 있으면 true |


null 허용(보통 1개)HashSetLinkedHashSetTreeSetHashMap 기반null 1개 저장 가능여기서 쓰이는 Hash라는 용어는 '데이터를 빠르게 저장하고 검색하기 위해서 사용하는 특별한 값 또는 기법'을 말하는데, 이는 데이터를 '고유한 숫자 값'으로 변환하는 과정을 말한다.
=> 여기서 고유한 숫자 = hashCode() 값
LinkedHashMap 기반(해시 + 연결 리스트)null 1개 저장 가능Comparator 사용 가능null 저장 불가(정렬/비교 불가능)1) 추가(Add)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | add(E e) | 요소 추가(중복이면 추가되지 않음, 성공 여부 반환) |
boolean | addAll(Collection<? extends E> c) | 컬렉션의 모든 요소를 Set에 추가 |
2) 삭제 / 비우기(Remove / Clear)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | remove(Object o) | 특정 요소 삭제(삭제 성공 여부 반환) |
void | clear() | Set의 모든 요소 삭제 |
3) 포함 여부(Contains)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | contains(Object o) | 특정 요소가 포함되어 있는지 확인 |
boolean | containsAll(Collection<?> c) | 컬렉션 c의 모든 요소가 Set에 포함되어 있는지 확인 |
4) 복사(Copy)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
static <E> Set<E> | copyOf(Collection<? extends E> coll) | 주어진 컬렉션을 복사해 새 Set을 반환(원본과 별개) |
5) 비교 / 동등성(Equals / Hash)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | equals(Object o) | Set 동등성 비교(순서가 아니라 “구성 요소” 기준) |
int | hashCode() | 해시값 반환(동등성 규약과 연계) |
6) 상태 확인
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
int | size() | Set의 크기(요소 수) 반환 |
boolean | isEmpty() | Set이 비어 있으면 true |
7) 순회(Iteration)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
Iterator<E> | iterator() | Set을 순회할 수 있는 Iterator 반환 |
Iterator는 한마디로 컬렉션(Set/List 등)을 "하나씩 꺼내서 순회할 수 있게 해주는 도구(반복자)"로 Set은 인덱스가 없어서(get 메서드 같은 게 없음) 내부 요소를 하나씩 보려면 순회 방법이 필요하고, 그 표준 방식이 iterator()이다.
Set<String> set = new HashSet<>();
set.add("A");
set.add("B");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String v = it.next();
System.out.println(v);
}
for each 구문 또한 iterator 기반의 방법이다.
null 허용 여부는 구현체에 따라 다름(TreeMap은 null key 불가)HashMapLinkedHashMapTreeMapnull 키 1개 허용, null 값 여러 개 허용null 키 1개 허용, null 값 여러 개 허용Comparator로 사용자 정의 정렬 가능null 키 허용하지 않음1) 추가/수정(Put & Compute)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
V | put(K key, V value) | 키-값 쌍 추가(키가 이미 있으면 값이 덮어써짐) |
default V | compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) | 키에 대해 새 값을 재계산(없으면 null로 계산될 수 있음) |
default V | computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) | 키가 없을 때만 값 생성 후 저장 |
default V | computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) | 키가 있을 때만 값 재계산 후 저장 |
2) 조회(Get)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
V | get(Object key) | 키에 해당하는 값을 반환(없으면 null) |
default V | getOrDefault(Object key, V defaultValue) | 키가 없으면 기본값 반환 |
3) 삭제(Remove)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
V | remove(Object key) | 키에 해당하는 엔트리 삭제 후 기존 값 반환(없으면 null) |
4) 포함 여부(Contains)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | containsKey(Object key) | 특정 키가 존재하는지 확인 |
boolean | containsValue(Object value) | 특정 값이 존재하는지 확인 |
5) 키/값/엔트리 뷰(View)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
Set<K> | keySet() | 모든 키를 Set 형태로 반환 |
Collection<V> | values() | 모든 값을 Collection 형태로 반환 |
Set<Map.Entry<K,V>> | entrySet() | (키,값) 엔트리 집합을 Set 형태로 반환 |
6) 복사(Copy)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
static <K,V> Map<K,V> | copyOf(Map<? extends K, ? extends V> map) | 주어진 Map을 복사해 새 Map을 반환(원본과 별개) |
7) 상태 확인
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
int | size() | 저장된 키-값 쌍(엔트리) 개수 반환 |
boolean | isEmpty() | Map이 비어 있으면 true |
void | clear() | 모든 엔트리 삭제 |
8) 반복/순회(Iteration)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
default void | forEach(BiConsumer<? super K, ? super V> action) | 모든 엔트리에 대해 (key,value)로 순회 수행 |
9) 동등성(Equals / Hash)
| 반환 타입 | 메서드 | 설명 |
|---|---|---|
boolean | equals(Object o) | Map 동등성 비교(엔트리 구성 기준) |
int | hashCode() | 해시값 반환 |
Arrays.sort()Collections.sort()// 배열 정렬
Arrays.sort(arr);
// 리스트 정렬
Collections.sort(list);
=> 실제 정렬은 모두 오름차순이 된다. 따라서 다른 정렬 기준으로 만들기 위해서 '정렬기준'가 필요하다.
자바에서는 객체 정렬 기준을 정의하는 방법은 두가지로 아래와 같다.
implemets Comparable<T>로 구현public interface Comparable<T> {
public int compareTo(T o);
}
반환값의 의미
- 0 : 현재 객체와 비교 대상 객체가 같음
- 양수 : 현재 객체가 비교 대상 객체보다 큼
- 음수 : 현재 객체가 비교대상 객체보다 작음
// 구현예시
import java.util.Objects;
class Student implements Comparable<Student> {
private final String name;
private final int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 기본 정렬 기준: age 오름차순
@Override
public int compareTo(Student other) {
return Integer.compare(this.age, other.age);
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Min", 23));
students.add(new Student("Yonu", 20));
students.add(new Student("Soo", 21));
Collections.sort(students); // compareTo 기준으로 정렬
System.out.println(students);
// 출력 예: [Yonu(20), Soo(21), Min(23)]
}
}
public interface Comparator<T> {
int compare(T o1, T o2);
}
반환값의 의미
- 0 : 두 객체가 같음
- 양수 : 첫 번째 객체가 두 번째 객체보다 큼
- 음수 : 첫 번째 객체가 두 번째 객체보다 작음
// 별도 클래스로 만들기
Collections.sort(names, new StringLengthComparator());
// 익명 내부 클래스로 만들기
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}
});
//람다로 만들기
Collections.sort(names, (o1, o2) -> {
return Integer.compare(o1.length(), o2.length());
});
class Student {
private final String name;
private final int age;
private final double gpa;
public Student(String name, int age, double gpa) {
this.name = name;
this.age = age;
this.gpa = gpa;
}
public String getName() { return name; }
public int getAge() { return age; }
public double getGpa() { return gpa; }
@Override
public String toString() {
return name + "(age=" + age + ", gpa=" + gpa + ")";
}
}
// 별도 클래스로 만들기
import java.util.Comparator;
class AgeAscComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
// o1이 앞이면 음수, o2가 앞이면 양수, 같으면 0
return Integer.compare(o1.getAge(), o2.getAge());
}
}
// 익명 클래스
import java.util.*;
Comparator<Student> nameAsc = new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName()); // 이름 오름차순
}
};
//람다
import java.util.*;
Comparator<Student> ageAsc = (o1, o2) -> Integer.compare(o1.getAge(), o2.getAge());
Comparator<Student> gpaDesc = (o1, o2) -> Double.compare(o2.getGpa(), o1.getGpa());
| 구분 | Comparable | Comparator |
|---|---|---|
| 목적 | 객체의 기본 정렬 기준(자연 순서) 을 클래스 내부에 정의 | 객체 외부에서 별도의 정렬 기준을 정의 |
| 구현 위치 | 정렬 대상 클래스가 implements Comparable<T> | 별도 클래스/익명 클래스/람다로 Comparator<T> 구현 |
| 핵심 메서드 | int compareTo(T o) | int compare(T o1, T o2) |
| 정렬 호출 | Collections.sort(list) / Arrays.sort(arr) | Collections.sort(list, comp) / Arrays.sort(arr, comp) |
| 정렬 기준 개수 | 보통 1개(대표 기준) | 여러 개 가능 (상황별 기준 교체 쉬움) |
| 클래스 수정 필요 | 필요함 (클래스에 compareTo 추가) | 불필요 (클래스 밖에서 정의) |
| 재사용성 | “기본 기준”은 어디서나 자동 적용되어 편함 | 기준을 필요한 곳에서만 선택적으로 적용 가능 |
| 유지보수 | 기준이 바뀌면 클래스 수정/재배포 필요 | 기준 변경/추가가 쉬움(정렬 코드만 바꾸면 됨) |
| 장점 | - 기본 정렬이 명확해짐 - 사용이 간단함(컴퍼레이터 없이 정렬 가능) | - 다양한 정렬 기준을 유연하게 적용 - 클래스 변경 없이 정렬 기준 추가 가능 - 다중 정렬( thenComparing)에 강함 |
| 단점 | - 정렬 기준이 여러 개면 관리가 불편함 - 클래스에 정렬 로직이 섞임 | - 매번 Comparator를 전달해야 할 수 있음 - 기준이 많아지면 Comparator 관리가 필요 |
| 언제 쓰나 | “이 클래스의 대표 정렬 기준이 명확”할 때 예: 학번, 날짜, id 등 | “정렬 기준이 상황에 따라 달라짐/여러 개 필요”할 때 예: 이름순/나이순/평점순 등 |
조금 복잡했지만 오늘의 개념 끝.