Java를 활용하다 보면 데이터를 순차적으로 처리하거나, 특정 순서에 따라 데이터를 관리해야 할 때가 있습니다. 이때 사용할 수 있는 자료구조가 큐(Queue)입니다.
이 글에서는 Java의 큐(Queue)에 대해 알아보고, 사용 방법과 예제를 통해 그 장단점을 살펴보겠습니다.
선형구조_큐(Queue)
큐는 선형구조 중 하나입니다. 선형 자료구조는 데이터가 순차적으로 배치되고 접근되는 구조를 의미합니다.
큐(Queue)의 동작방식
위 그림에서 보면 알 수 있듯이 큐(Queue)는 선형 구조의 형태를 띠고 있습니다.
큐는 다음과 같은 특성을 가지며, 다음과 같은 특징을 통해 선형 자료구조임을 알 수 있습니다.
- 선형 배치
- 큐는 데이터를 선형적으로 배치합니다. 즉, 각 데이터는 일렬로 나열되며, 특정 순서에 따라 삽입과 삭제가 이루어집니다.
- 순차적 접근
- 큐는 데이터를 순차적으로 접근합니다. 첫 번째로 들어온 데이터가 첫 번째로 나가고, 그 다음 데이터가 그다음에 나가는 방식입니다.
- FIFO(First-In-First-Out)
- 큐는 선입선출 방식을 따릅니다. 먼저 삽입된 데이터가 먼저 삭제됩니다.
- 두 가지 주요 연산
- Enqueue: 큐에 요소를 추가하는 작업입니다. 일반적으로 큐의 끝(리어, rear)에 새로운 요소가 추가됩니다.
- Dequeue: 큐에서 요소를 제거하는 작업입니다. 일반적으로 큐의 앞(프런트, front)에서 요소가 제거됩니다.
- Front와 Rear
- Front: 큐의 맨 앞 요소를 가리키는 포인터입니다. Dequeue 연산이 발생할 때 변경됩니다.
- Rear: 큐의 맨 끝 요소를 가리키는 포인터입니다. Enqueue 연산이 발생할 때 변경됩니다.
- 한 방향으로 데이터 이동
- 큐는 데이터의 삽입과 삭제가 같은 방향에서만 발생합니다.
- 이러한 특성으로 인해 데이터의 삽입과 삭제가 O(1)의 시간 복잡도로 수행될 수 있습니다.
큐(Queue)의 선언방식
큐(Queue)는 다양한 방법으로 큐를 선언할 수 있습니다. 자바에서는 java.util.Queue 인터페이스를 통해 여러 구현체를 제공합니다.
기본 LinkedList를 사용한 큐 선언
Queue<Integer> queue1 = new LinkedList<>();
ArrayDequeue를 사용한 큐 선언
Queue<Integer> queue2 = new ArrayDeque<>();
PrioriyQueue를 사용한 큐 선언
Queue<Integer> queue3 = new PriorityQueue<>();
큐(Queue)의 장단점
장점
- FIFO 특성
- 큐는 선입선출 방식으로 데이터를 처리하여 순서가 중요한 작업에 적합합니다.
- 쉬운 구현
- 자바에서 큐는 LinkedList나 ArrayDeque 등의 클래스만으로 쉽게 구현할 수 있습니다.
- 다양한 구현체
- PriorityQueue 등 다양한 구현체를 통해 특수한 목적의 큐를 사용할 수 있습니다.
단점
- 순차 접근만 가능
- 큐는 데이터를 순차적으로 접근해야 하므로
임의 접근이 불가능합니다.
- 큐는 데이터를 순차적으로 접근해야 하므로
- 성능
- LinkedList를 사용한 큐는 삽입과 삭제가 빠르지만, ArrayDeque는 메모리 재할당이 발생할 수 있습니다.
큐(Queue)의 메서드
add(E e)
import java.util.Queue;
import java.util.LinkedList;
public class AddMethodExample {
public static void main(String[] args) {
// Queue 생성
Queue<Integer> queue = new LinkedList<>();
// 요소 추가
queue.add(1);
queue.add(2);
queue.add(3);
// 큐 출력
System.out.println("큐 상태: " + queue); // 출력: [1, 2, 3]
try {
// 용량 초과로 예외 발생하는 경우
queue.add(4);
queue.add(5);
queue.add(6);
queue.add(7);
queue.add(8); // 여기서 예외 발생: IllegalStateException: Queue full
} catch (IllegalStateException e) {
System.out.println("add() 메서드 예외 발생: " + e.getMessage());
}
}
}
add() 메서드는 Queue 인터페이스에서 상속된 메서드이며, 요소를 큐에 추가할 때 사용됩니다.
일반적으로 큐의 용량이 충분할 때는 정상적으로 요소를 추가하지만, 용량 초과 시 IllegalStateException을 발생시킵니다. 따라서 따로 예외처리를 해줘야합니다.
offer(E e)
import java.util.Queue;
import java.util.LinkedList;
public class OfferExample {
public static void main(String[] args) {
// 큐 생성
Queue<Integer> queue = new LinkedList<>();
// 요소 추가
queue.offer(1);
queue.offer(2);
queue.offer(3);
// 큐 상태 출력
System.out.println("큐에 요소 추가 후 상태: " + queue); // 출력: [1, 2, 3]
}
}
offer() 메서드는 데이터를 큐에 추가할 때 사용합니다.
add() 메서드와 동일한 기능을 하지만 add()와 다르게 예외가 발생하지 않고 큐가 공간 부족으로 데이터를 추가하지 못할 경우 false를 반환할 수 있습니다.
poll()
import java.util.Queue;
import java.util.LinkedList;
public class PollMethodExample {
public static void main(String[] args) {
// Queue 생성
Queue<Integer> queue = new LinkedList<>();
// 요소 추가
queue.offer(1);
queue.offer(2);
queue.offer(3);
// poll() 메서드를 사용하여 요소 제거 및 반환
System.out.println("첫 번째 제거된 요소: " + queue.poll()); // 출력: 1
System.out.println("두 번째 제거된 요소: " + queue.poll()); // 출력: 2
System.out.println("세 번째 제거된 요소: " + queue.poll()); // 출력: 3
// 큐가 비어있는 경우
System.out.println("네 번째 제거된 요소: " + queue.poll()); // 출력: null
}
}
poll() 메서드는 큐에서 요소를 제거하고 반환하는 메서드입니다. 큐의 가장 앞에 있는 요소를 제거하고 그 값을 반환합니다. 만약 큐가 비어있으면 null을 반환합니다.
peek()
import java.util.Queue;
import java.util.LinkedList;
public class PeekExample {
public static void main(String[] args) {
// 큐 생성 및 요소 추가
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);
queue.offer(2);
queue.offer(3);
// peek()을 사용하여 요소 조회
int peekedElement = queue.peek();
// 조회된 요소와 큐 상태 출력
System.out.println("조회된 요소: " + peekedElement); // 출력: 1 (첫 번째 요소를 조회)
System.out.println("peek() 이후 큐 상태: " + queue); // 출력: [1, 2, 3] (큐 상태 변화 없음)
}
}
peek() 메서드는 큐에서 가장 앞에 있는 요소를 조회하는 메서드입니다. 이 메서드는 요소를 제거하지 않고 단순히 조회만 합니다. 만약 큐가 비어있으면 null을 반환합니다.
element()
import java.util.Queue;
import java.util.LinkedList;
import java.util.NoSuchElementException;
public class ElementMethodExample {
public static void main(String[] args) {
// Queue 생성
Queue<Integer> queue = new LinkedList<>();
// 요소 추가
queue.offer(1);
queue.offer(2);
queue.offer(3);
try {
// element() 메서드를 사용하여 요소 조회
System.out.println("첫 번째 요소 조회: " + queue.element()); // 출력: 1
System.out.println("element() 호출 후 큐 상태: " + queue); // 출력: [1, 2, 3]
// 두 번째 요소 조회
int secondElement = queue.element();
System.out.println("두 번째 요소 조회: " + secondElement); // 출력: 1 (아직 요소는 제거되지 않았음)
System.out.println("element() 호출 후 큐 상태: " + queue); // 출력: [1, 2, 3]
// 세 번째 요소 조회
System.out.println("세 번째 요소 조회: " + queue.element()); // 출력: 1 (아직 요소는 제거되지 않았음)
System.out.println("element() 호출 후 큐 상태: " + queue); // 출력: [1, 2, 3]
// 큐가 비어있는 경우 (NoSuchElementException 발생)
queue.clear(); // 큐를 비움
System.out.println("element() 호출 (큐가 비어있음): " + queue.element());
} catch (NoSuchElementException e) {
System.out.println("element() 메서드 예외 발생: " + e.getMessage());
}
}
}
element() 메서드는 큐에서 가장 앞에 있는 요소를 조회하는 메서드입니다. 이 메서드는 peek() 메서드와 비슷한 역할을 하지만, 큐가 비어있을 경우 NoSuchElementException을 발생시킵니다.
따라서 요소가 있는지 여부를 확인할 때 유용합니다. 다만, 큐가 비어있을 경우 예외가 발생하므로 반드시 예외 처리를 해주어야 합니다.
remove()
import java.util.Queue;
import java.util.LinkedList;
public class RemoveMethodWithoutExceptionExample {
public static void main(String[] args) {
// Queue 생성
Queue<Integer> queue = new LinkedList<>();
// 요소 추가
queue.offer(1);
queue.offer(2);
queue.offer(3);
// isEmpty() 메서드로 큐가 비어있는지 확인하고 요소 제거
while (!queue.isEmpty()) {
int removedElement = queue.remove();
System.out.println("제거된 요소: " + removedElement);
System.out.println("remove() 호출 후 큐 상태: " + queue);
}
// isEmpty() 메서드로 큐가 비어있는지 확인 (요소가 없는 경우)
if (queue.isEmpty()) {
System.out.println("큐가 비어있습니다.");
// 추가적인 처리 가능
} else {
// 큐가 비어있지 않은 경우 처리
int removedElement = queue.remove();
System.out.println("제거된 요소: " + removedElement);
}
}
}
remove() 메서드는 큐에서 요소를 제거하고 그 값을 반환하는 기능을 수행합니다. 요소가 없는 상태에서 호출하면 예외를 발생시키므로, 반드시 예외 처리를 해주어야 합니다.
isEmpty()
import java.util.Queue;
import java.util.LinkedList;
public class QueueIsEmptyExample {
public static void main(String[] args) {
// Queue 생성
Queue<Integer> queue = new LinkedList<>();
// isEmpty() 메서드로 큐가 비어있는지 확인
System.out.println("큐가 비어있는지 확인: " + queue.isEmpty()); // 출력: true
// 요소 추가
queue.offer(1);
queue.offer(2);
queue.offer(3);
// isEmpty() 메서드로 큐가 비어있는지 확인
System.out.println("큐가 비어있는지 확인: " + queue.isEmpty()); // 출력: false
// 요소 제거
while (!queue.isEmpty()) {
int removedElement = queue.poll();
System.out.println("제거된 요소: " + removedElement);
}
// 모든 요소를 제거한 후 isEmpty() 메서드로 큐가 비어있는지 확인
System.out.println("큐가 비어있는지 확인: " + queue.isEmpty()); // 출력: true
}
}
큐(Queue)의 isEmpty() 메서드는 큐가 비어있는지 여부를 확인하는 메서드입니다. 큐가 비어있으면 true를 반환하고, 요소가 하나라도 있으면 false를 반환합니다.
isEmpty() 메서드는 큐의 상태를 확인하거나 조작하기 전에 큐가 비어있는지 여부를 검사하는 데 사용됩니다. 이를 통해 요소를 추가하거나 제거할 때 불필요한 예외 처리를 줄일 수 있습니다.
clear()
import java.util.*;
public class QueueClearExample {
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
// 큐에 요소 추가
queue.offer(1);
queue.offer(2);
queue.offer(3);
System.out.println("큐 초기 상태: " + queue);
// clear 메서드로 큐 비우기
queue.clear();
System.out.println("큐 비운 후 상태: " + queue);
}
}
clear() 메서드는 큐에서 모든 요소를 제거하여 큐를 비웁니다.
큐(Queue)는 언제 사용할까?
큐는 다음과 같은 상황에서 유용하게 사용됩니다
- 작업 스케줄링
- 작업을 순차적으로 처리할 때
- 너비 우선 탐색(BFS)
- 그래프나 트리 탐색에서
- 데이터 스트리밍
- 실시간 데이터 스트리밍 처리에서
- 프린터 대기열
- 인쇄 작업을 순서대로 처리할 때
'자료구조' 카테고리의 다른 글
[JAVA] HashMap_entrySet() : 데이터 접근과 반복을 간편하게 하는 방법(2/2) (0) | 2024.06.28 |
---|---|
[자료구조 JAVA] 우선순위 큐(Priority Queue) 클래스 알아보기 ✔ (0) | 2024.06.16 |
[자료구조 JAVA] LinkedHashSet 알아보기 (1/2) (0) | 2024.06.15 |
[자료구조 JAVA] 스택 Stack 컬렉션 알아보기 (1/2) (0) | 2024.06.13 |
[JAVA] HashMap 이란 ❓ (1/2) (1) | 2024.06.05 |