Java Collection Framework
컴퓨터 공학 자료 구조 과목 : List , Set , Map, Stack, Queue란?
public static void main(String[] args) {
exampleList();
exampleSet();
exampleMap();
exampleStackUsingDeque();
exampleQueueUsignDeque();
examplePriorityQueue();
}
각 메서드를 만들어서 컬렉션에 대해 다뤄보도록 하겠다.
이번 목표는 개념을 이해하고 상황에 맞게 적절한 활용 예시이다.
private static void exampleList() {
System.out.println("-------List start-------");
List<String> list = new ArrayList<>();
list.add("zebra");
list.add("apple");
list.add("monkey");
list.add("banana");
list.add("cherry"); //중복 가능, 순서 o
System.out.println("////List :" + list);
System.out.println("////List 1번째 :" + list.get(1));
// 삽입
list.add(1, "X");
System.out.println("////List 1삽입 :" + list); // 결과: 삽입하고 뒤에 밀림
// 삭제
list.remove(2);
System.out.println("////List 2삭제 :" + list);
System.out.println();
}
why?
사용자가 같은 상품을 두 번 담을 수 있다(중복 허용)
첫 번째 담은 상품처럼 인덱스로 접근이 가능해야 한다.
List<String> cart = new ArrayList<>();
cart.add("노트북");
cart.add("마우스");
cart.add("노트북"); // 중복 허용
System.out.println("장바구니: " + cart); // [노트북, 마우스, 노트북]
System.out.println("첫 번째 상품: " + cart.get(0)); // 노트북
private static void exampleSet() {
System.out.println("=== Set (HashSet / LinkedHashSet / TreeSet) ===");
// HashSet: 가장 흔함, 빠름, '순서 없음'
Set<String> hashSet = new HashSet<>();
hashSet.add("B");
hashSet.add("A");
hashSet.add("B"); // 중복은 무시
System.out.println("HashSet (순서X) = " + hashSet);
// LinkedHashSet: '입력 순서 유지'
Set<String> linkedSet = new LinkedHashSet<>();
linkedSet.add("B");
linkedSet.add("A");
linkedSet.add("B"); // 중복 무시
System.out.println("LinkedHashSet (입력순서 유지) = " + linkedSet); // [B, A]
// TreeSet: '자동 정렬' (기본 오름차순), null 불가
Set<String> treeSet = new TreeSet<>();
treeSet.add("B");
treeSet.add("A");
treeSet.add("C");
System.out.println("TreeSet (정렬됨) = " + treeSet); // [A, B, C]
// 언제 쓰나: 중복을 자동으로 제거해야 할 때(유니크한 값 모으기: 이메일, 태그 등)
// 주의: HashSet은 출력 순서가 예측 불가 → 화면 표시용이면 LinkedHashSet 사용
System.out.println();
}
why?
중복 자동 제거, 순서 필요없다는 특징
Set<String> seats = new HashSet<>();
seats.add("A1");
seats.add("A2");
seats.add("A1"); // 중복 -> 무시됨
System.out.println("예약된 좌석: " + seats); // [A1, A2]
입력 순서를 유지해야 한다면 LinkedHashSet
좌석 번호를 자동 정렬하려면 TreeSet을 사용하면 된다.
private static void exampleMap() {
System.out.println("=== Map (HashMap / LinkedHashMap / TreeMap) ===");
// HashMap: 빠름, 순서 없음
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("apple", 3); // 키 "apple" → 값 3
hashMap.put("banana", 1);
hashMap.put("apple", 10); // 같은 키에 put → 기존 값 3을 10으로 '덮어쓰기'
System.out.println("HashMap size = " + hashMap.size()); // 2
System.out.println("HashMap get('apple') = " + hashMap.get("apple")); // 10
// LinkedHashMap: 입력 순서 유지(화면 표시용에 좋음)
Map<String, Integer> linkedMap = new LinkedHashMap<>();
linkedMap.put("apple", 3);
linkedMap.put("banana", 1);
linkedMap.put("cherry", 2);
System.out.println("LinkedHashMap (입력순서 유지) = " + linkedMap); // {apple=3, banana=1, cherry=2}
// TreeMap: 키 기준 정렬(사전순 등)
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("banana", 1);
treeMap.put("apple", 3);
treeMap.put("cherry", 2);
System.out.println("TreeMap (키 정렬) = " + treeMap); // {apple=3, banana=1, cherry=2}
// Map 순회: entrySet()으로 키와 값을 함께 꺼내면 편함
System.out.println("Iterate LinkedHashMap:");
for (Map.Entry<String, Integer> e : linkedMap.entrySet()) {
System.out.println(" " + e.getKey() + " -> " + e.getValue());
}
// 언제 쓰나: 기준(키)로 빠르게 찾아야 할 때(아이디→회원정보, 상품코드→상품)
// 주의: 같은 키 put 시 덮어쓰기, HashMap은 순서X, TreeMap은 null 키 불가
System.out.println();
}
why?
키를 통해 빠르게 값 검색, 같은 이름이 들어오면 점수 덮어씀
단, 동명이인일 경우에는??
-> Map은Key가 유일해야 한다는 규칙이 있다.
그래서 HashMap, TreeMap, LinkedHashMep 전부 동일한 키가 들어오면 기존 값을 덮어쓴다.
그래서 동명이인일 경우에는 이름을 Key로 쓰지 않고 학번, ID같은 고유 식별자를 Key로 두면 된다.
// - 레거시 java.util.Stack 대신 Deque(ArrayDeque) 권장
private static void exampleStackUsingDeque() {
System.out.println("=== Stack (using Deque/ArrayDeque) ===");
// Deque을 스택처럼 사용: push/pop/peek
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); // 맨 위에 1
stack.push(2); // 맨 위에 2
stack.push(3); // 맨 위에 3 (최상단)
System.out.println("pop() -> " + stack.pop()); // 3 (가장 나중에 넣은 것)
System.out.println("peek() -> " + stack.peek()); // 2 (위에 무엇이 있는지 보기만)
// 언제 쓰나: 실행취소(UNDO), 역순 처리, 괄호검사 등 LIFO 로직
// 왜 Deque? Stack은 동기화 등 오버헤드가 있고 레거시라 비권장. Deque가 더 가볍고 빠름.
System.out.println();
}
why?
마지막 동작부터 취소해야 함(LIFO)
Deque<String> undoStack = new ArrayDeque<>();
undoStack.push("글자 입력");
undoStack.push("굵게 적용");
undoStack.push("밑줄 적용");
System.out.println("실행 취소: " + undoStack.pop()); // 밑줄 적용 취소
System.out.println("다음 취소: " + undoStack.peek()); // 굵게 적용 (보기만)
// - 보통 Deque(ArrayDeque)로 큐 연산(offer/poll/peek)
private static void exampleQueueUsingDeque() {
System.out.println("=== Queue (using Deque/ArrayDeque) ===");
Deque<String> queue = new ArrayDeque<>();
queue.offer("A"); // 뒤로 A
queue.offer("B"); // 뒤로 B
queue.offer("C"); // 뒤로 C
System.out.println("poll() -> " + queue.poll()); // A (가장 먼저 들어온 것)
System.out.println("peek() -> " + queue.peek()); // B (앞에 무엇이 있는지 보기만)
// 언제 쓰나: 작업 대기열, BFS, 생산자-소비자(동시성 환경에서는 BlockingQueue 계열 사용)
System.out.println();
}
why?
FIFO구조가 딱 맞음
Deque<String> waitingQueue = new ArrayDeque<>();
waitingQueue.offer("손님1");
waitingQueue.offer("손님2");
waitingQueue.offer("손님3");
System.out.println("처리: " + waitingQueue.poll()); // 손님1
System.out.println("다음 대기: " + waitingQueue.peek()); // 손님2
System.out.println("=== PriorityQueue (기본 오름차순) ===");
// 작은 값이 먼저 나온다(기본 자연 순서)
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(5);
pq.offer(1);
pq.offer(3);
// poll()할 때마다 가장 '작은' 값부터 빠짐 → 1, 3, 5
System.out.print("poll order = ");
while (!pq.isEmpty()) {
System.out.print(pq.poll() + " ");
}
System.out.println();
// 내림차순 우선순위가 필요하면 Comparator 넘기기
Queue<Integer> maxPq = new PriorityQueue<>(Comparator.reverseOrder());
maxPq.offer(5);
maxPq.offer(1);
maxPq.offer(3);
System.out.print("reverse poll order = ");
while (!maxPq.isEmpty()) {
System.out.print(maxPq.poll() + " "); // 5, 3, 1
}
System.out.println();
// 언제 쓰나: “중요한 것 먼저 처리” (예: 가장 높은 점수, 가장 긴 대기시간 등)
System.out.println();
}
}
why?
우선순위 높은 요소 먼저 꺼내야 함
class Customer {
String name;
int priority; // 숫자가 작을수록 우선순위 높음
Customer(String name, int priority) {
this.name = name;
this.priority = priority;
}
public String toString() { return name; }
}
PriorityQueue<Customer> pq = new PriorityQueue<>(Comparator.comparingInt(c -> c.priority));
pq.offer(new Customer("일반 고객", 5));
pq.offer(new Customer("VIP 고객", 1));
pq.offer(new Customer("일반 고객2", 3));
while (!pq.isEmpty()) {
System.out.println("응대: " + pq.poll()); // VIP → 일반 고객2 → 일반 고객
}