1. 컬렉션(Collection) 이란?
컬렉션(Collection)은 자바에서 우리가 자주 사용하는 자료 구조를 사용하기 쉽게 클래스로 구현해놓은 것이다.
이 컬렉션을 구조화해서 우리에게 인터페이스로 제공해주는 것이 JCF(Java Collection Framework)이다.
컬렉션을 사용하면, 이미 최적화가 잘 되어있는 코드를 사용하기 때문에 객체 지향적이고 재사용성이 높은 코드로 프로그래밍할 수 있기 때문에 편리하다. 그리고 일일이 자료구조를 구현할 필요가 없기 때문에 시간도 절약된다.
2. 컬렉션 프레임워크 구조

컬렉션 프레임워크는 Collection 인터페이스를 확장받고 있는 List, Set, Queue가 있고,
일반 Collection을 확장 받지는 않지만 같이 묶어서 말하는 Map 인터페이스가 따로 있다.
Map이 따로 있는 이유는 Collection 인터페이스와의 개념적 차이와 사용 방식의 차이로 인해 별도의 인터페이스로 설계되었다.
3. 언제 사용해야할까?
| 인터페이스 | 구현 클래스 | 특징 |
| List | ArrayList, LinkedList, Vector, Stack |
- 저장 순서가 유지 O - 중복 저장 O |
| Set | SortedSet, HashSet, TreeSet |
- 저장 순서 유지 X (구현 클래스마다 다름) - 중복 저장 X |
| Queue | LinkedList, PriorityQueue | - List 인터페이스와 유사하다. |
| Map | HashMap, LinkedHashMap, HashTable, TreeMap | - 저장 순서 유지 X - 키(key)와 값(value)의 쌍으로 연관지어 이루어진 데이터의 집합 - 값(value)은 중복 허용 O, 키(key)는 중복 허용 X |
요소들의 삽입 순서가 중요하고 중복된 요소가 있을 때 -> List
요소가 객체 집합으로 처리되고, 중복이 없으며 삽입 순서가 중요하지 않을 때 -> Set
LIFO, FIFO 또는 우선순위에 따라 제거가 필요할 때 -> Queue
키(key)와 값(value)의 연관이 필요할 때 -> Map
4. List
1) LinkedList
선언 시 크기를 변경할 수 없고, 비순차적인 데이터의 삽입/삭제에 시간이 많이 걸리는 단점을 가진 배열과 달리
LinekdList는 크기 변경이 자유롭고(가변), 삽입/삭제가 편리하다.
빠른 삽입/삭제가 필요할 때 선택
[특징]
- 불연속적으로 존재하는 각 노드가 이전 및 다음 노드를 참조하는 구조로 이루어져있는 양방향 포인터 구조다.
- 각 요소가 독립적인 노드로 관리되며, 인덱스로 접근할 때는 리스트의 앞이나 뒤에서부터 순차적으로 탐색한다.
- 데이터 삽입/삭제 시 데이터의 위치 정보만 수정하면 되므로 빈번한 삽입/삭제 작업에 유리하다.
- poll(), pop(), push() 등의 메서드로 큐와 스택의 동작을 수행할 수 있다.
import java.util.LinkedList;
public class Sample {
public static void main(String[] args) {
LinkedList<String> linkedList= new LinkedList<String>(); // 빈 LinkedList 생성
linkedList.add("one"); // 뒤에서 삽입
linkedList.addFirst("two"); // 앞에서 삽입
linkedList.add("three"); // 뒤에서 삽입
System.out.println(linkedList.get(0)); // two
System.out.println(linkedList.get(1)); // one
System.out.println(linkedList.get(2)); // three
}
}
2) ArrayList
List 인터페이스를 구현한 가변 크기 배열 기반 컬렉션이다. 동적으로 크기를 조정할 수 있으며, 배열보다 유연한 데이터 관리가 가능하다. Vector의 업그레이드 버전이라고 할 수 있다.
삽입/삭제가 많지 않을 때 기본적으로 선택
[특징]
- Object 배열을 기반으로 만들어졌다.
- 데이터를 순차적으로 저장하며 중복을 허용한다.
- 단방향 포인터 구조로 데이터에 인덱스가 부여되기 때문에 조회가 빠르다.
- 중간에 요소를 삽입/삭제할 때는 이후의 요소들이 한 칸씩 이동해야 하므로 삽입/삭제 작업에 불리하다.
import java.util.ArrayList;
public class Sample {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(); // 빈 ArrayList 생성
ArrayList<Integer> list2 = new ArrayList<>(10); // 초기 용량 지정 가능
list.add("Apple");
list.addFirst("Banana"); // 왼쪽에서 삽입
list.addLast("Cherry"); // 오른쪽에서 삽입
list.add(1, "Blueberry"); // 특정 위치에 추가
System.out.println(list); // [Banana, Blueberry, Apple, Cherry]
}
}
list.set(1, "Blackberry"); // 요소 변경
list.remove(2); // 인덱스로 삭제
list.remove("Cherry"); // 값으로 삭제
3)ArrayList vs LinkedList 비교
| 컬렉션 | 처음에 삽입/삭제 | 중간에 삽입/삭제 | 끝에 삽입/삭제 | 인덱스로 조회 | 요소 찾기 | 순서 |
| ArrayList | O(n) | O(n) | O(1) | O(1) | O(n) / O(log(n)) (정렬된 경우) | 유지 |
| LinkedList | O(1) | O(1) | O(1) | O(n) | O(n) | 유지 |
4) Vector / Stack
[Vector]
- ArrayList의 구 버전으로 ArrayList와 비슷하지만 잘 쓰이지 않는다.
- 내부적으로 자동 동기화가 되기 때문에 무겁고 성능이 좋지 않다.
- ArrayList도 동기화를 하는 방법이 있기 때문에 굳이 Vector를 안써도 된다.
[Stack]
- Vector를 상속하고 있다.
- LIFO 자료 구조다.
- 넣을 때는 push(), 뺄 때는 pop() 메서드를 사용한다.
- ArrayDeque같은 다른 클래스로 구현하는 경우가 많다.
5. Set
1) HashSet
Set 인터페이스에서 지원하는 해시 기반(HashMap)의 컬렉션이다.
기본적으로 선택.
[특징]
- 객체를 중복하여 저장할 수 없으며, null을 한 개 허용한다.
- 요소의 순서를 보장하지 않는다.
- 해시 기반이기 때문에 Set 인터페이스에서 조회 성능이 가장 좋다.
- 삽입/삭제 성능 또한 제일 좋다.
import java.util.HashSet;
import java.util.Set;
public class Sample {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>(); // 빈 HashSet 생성
HashSet<Integer> hashset2 = new HashSet<>(Set.of(1, 2, 3, 4, 5)); // 초기 값 추가
set.add("Apple");
set.add("Banana");
set.add("Cherry");
set.add("Apple"); // 중복 요소 → 추가되지 않음
System.out.println(set); // [Apple, Cherry, Banana] (순서 보장 X)
}
}
set.remove("Banana"); // 요소 제거
set.clear(); // 모든 요소 삭제
System.out.println(set.contains("Apple")); // False | 요소 포함 여부 확인
// HashSet을 TreeSet으로 변환할 수 있다.
Set<String> hashSet = new HashSet<>();
hashSet.add("Banana");
hashSet.add("Apple");
hashSet.add("Cherry");
Set<String> treeSet = new TreeSet<>(hashSet);
System.out.println(treeSet); // [Apple, Banana, Cherry] (정렬된 순서)
2) LinkedHashSet
HashSet과 거의 비슷하지만 Linked 방식을 이용하여 순서를 가지고 있는 Set이다.
순서가 중요하면 선택.
import java.util.LinkedHashSet;
import java.util.Set;
public class Sample {
public static void main(String[] args) {
Set<Integer> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add(10);
linkedHashSet.add(20);
linkedHashSet.add(30);
linkedHashSet.add(10); // 중복 허용 X
System.out.println(linkedHashSet); // [10, 20, 30] | 순서 유지
}
}
3) TreeSet
Set 인터페이스에서 지원하는 레드-블랙 트리(Red-Black Tree)기반 컬렉션이다.
정렬이 필요하면 선택.
[특징]
- 객체를 중복하여 저장할 수 없으며, null을 허용하지 않는다.
- 기본적으로 오름차순으로 정렬하여 저장한다.
- 모든 연산(삽입/삭제/조회)에 O(log n)의 시간복잡도를 가진다.
- 이진 탐색 트리로 구현되어 있다.
import java.util.Set;
import java.util.TreeSet;
public class Sample {
public static void main(String[] args) {
Set<String> treeSet = new TreeSet<>();
treeSet.add("Apple");
treeSet.add("Banana");
treeSet.add("Cherry");
System.out.println(treeSet); // [Apple, Banana, Cherry] (오름차순 정렬)
}
}
4) EnumSet
java.util 패키지에서 제공하는 Enum 클래스와 함께 동작하는 구현체이다.
[특징]
- 객체를 중복하여 저장할 수 없으며, null을 허용하지 않는다.
- 모든 메서드가 산술 비트 연산자로 구현되어 삽입/삭제/조회의 성능이 HashSet보다도 빠르다.
- Enum의 정의 순서대로 순서를 유지한다.
- Enum값만 포함할 수 있다.
- EnumSet은 추상 클래스고 이를 상속한 RegularEnumSet 혹은 JumboEnumSet 객체를 사용하게 된다.
import java.util.EnumSet;
enum Color {
RED, YELLOW, GREEN, BLUE, BLACK, WHITE
}
public class Sample {
public static void main(String[] args) {
EnumSet<Color> set = EnumSet.allOf(Color.class);
set.forEach(System.out::println); // RED, YELLOW, GREEN, BLUE, BLACK, WHITE
}
}
5) 각 Set 비교
| 컬렉션 | 추가 속도 | 삭제 속도 | 검색 속도 | 순서 유지 여부 |
| HashSet | O(1) | O(1) | O(1) | 랜덤 |
| LinkedHashSet | O(1) | O(1) | O(1) | 삽입 순서 유지 |
| TreeSet | O(log(n)) | O(log(n)) | O(log(n)) | 정렬된 순서 유지 |
| EnumSet | O(1) | O(1) | O(1) | Enum 정의 순서 유지 |
6. Queue
1) Queue
FIFO 원칙을 따르는 자료구조로 요소를 뒤(rear)에서 추가하고 앞(front)에서 제거하며 관리하는 자료구조이다.
Queue 인터페이스는 여러 구현체를 제공하며, 다중 스레드 환경에서도 유용하다.
일반적인 대기열 처리 시에 선택
| 반환 타입 | 주요 메서드 | 설명 |
| boolean | add(E e) | 요소 추가 (실패 시 IllegalStateException 반환) |
| boolean | offer(E e) | 요소 추가 (실패 시 False 반환) |
| object | remove() | 맨 앞 요소 반환 (비어 있으면 NoSuchElementException 반환) |
| object | poll() | 맨 앞 요소 제거 및 반환 (비어 있으면 null 반환) |
| object | element() | 맨 앞 요소 조회 (비어 있으면 NosuchElementException 반환) |
| object | peek() | 맨 앞 요소 조회 (비어 있으면 null 반환) |
import java.util.LinkedList;
import java.util.Queue;
public class Sample {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.offer("Apple"); // 요소 추가
queue.add("Banana"); // 요소 추가
System.out.println(queue.peek()); // Apple
System.out.println(queue.poll()); // Apple 제거
System.out.println(queue.remove()); // Banana 제거
Queue<String> queue2 = new LinkedList<>();
System.out.println(queue2.peek()); // null 출력
System.out.println(queue2.element()); // NoSuchElementException 출력
}
}
2) PriorityQueue
요소를 정렬된 상태로 유지하는 Queue이다.
가장 중요한 요소를 먼저 처리해야 할 때 선택
[특징]
- 요소에 우선 순위를 부여하고, 우선 순위가 높은 순으로 나간다.
- 요소를 힙(heap) 형태로 저장하며 null은 허용하지 않는다.
- Comparator를 지정하여 사용자 정의 정렬이 가능하다.
- Queue와 메소드가 유사하다.
import java.util.Collections;
import java.util.PriorityQueue;
public class Sample {
public static void main(String[] args) throws InterruptedException {
PriorityQueue<Integer> pq = new PriorityQueue<>(); // 오름차순 정렬
pq.add(9);
pq.add(2);
pq.add(4);
System.out.println(pq.poll()); // 2 (가장 작은 값)
System.out.println(pq.poll()); // 4
System.out.println(pq.poll()); // 9
//ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
PriorityQueue<Integer> pq2 = new PriorityQueue<>(Collections.reverseOrder()); // 내림차순 정렬
pq2.add(9);
pq2.add(2);
pq2.add(4);
System.out.println(pq2.poll()); // 9 (가장 큰 값)
System.out.println(pq2.poll()); // 4
System.out.println(pq2.poll()); // 2
}
}
3) Deque
양쪽에서 삽입/삭제가 가능한 Queue이다. Stack으로든 Queue로든 사용 가능하다. (둘 다 성능이 좋다.)
ArrayDeque나 LinkedList를 통해 구현한다.
사이즈의 제한이 없으며 null은 허용하지 않는다.
양쪽에서 삽입/삭제가 필요할 때 선택
| 주요 메서드 | 설명 |
| addFirst() / addLast() | 맨 앞에 요소 추가 / 맨 뒤에 요소 추가 (실패 시 예외 발생) |
| offerFirst() / offerLast() | 맨 앞에 요소 추가 / 맨 뒤에 요소 추가 (성공 시 True, 실패 시 False 반환) |
| removeFirst() / removeLast() | 맨 앞에 요소 제거 / 맨 뒤에 요소 제거 (해당 요소를 리턴하고 실패 시 예외 발생) |
| pollFisrt() / pollLast() | 맨 앞에 요소 제거 / 맨 뒤에 요소 제거 (해당 요소를 리턴하고 비어있으면 null 리턴) |
| getFirst() / getLast() | 맨 앞에 요소 반환 / 맨 뒤에 요소 반환 (해당 요소를 리턴하고 비어있으면 예외 발생) |
| peekFirst() / peekLast() | 맨 앞에 요소 반환 / 맨 뒤에 요소 반환 (해당 요소를 리턴하고 비어있으면 null 리턴) |
import java.util.ArrayDeque;
import java.util.Deque;
public class Sample {
public static void main(String[] args) throws InterruptedException {
Deque<String> deque = new ArrayDeque<>();
deque.addLast("A"); // [A]
deque.offerFirst("B"); // [B, A]
deque.addFirst("C"); // [C, B, A]
deque.offerLast("D"); // [C, B, A, D]
System.out.println(deque.pollFirst()); // C [B, A, D] | C 제거&반환
System.out.println(deque.pollLast()); // D [B, A] | D 제거&반환
System.out.println(deque.peekFirst()); // B [B, A] | B 반환
System.out.println(deque.getFirst()); // B [B, A] | B 반환
System.out.println(deque.getLast()); // A [B, A] | A 반환
}
}
출처
'JAVA' 카테고리의 다른 글
| [JAVA] ORM 이란? (0) | 2025.05.31 |
|---|---|
| [JAVA] JDBC 정리 (0) | 2025.05.27 |
| [JAVA] 스레드(Thread) 정리 (0) | 2025.03.01 |
| [JAVA] 컬렉션(Collection) 정리 / Map (0) | 2025.02.27 |
| [JAVA] 래퍼 클래스(Wrapper Class) 정리 (1) | 2025.02.16 |