Data Structure, Generic Stack & Queue
지난 시간까지 Stack & Queue를 Linked List와 Array 두 가지 데이터 구조로 구현해봤습니다.
Data Structure, Stack
Stack과 Queue, Hash Table 등 대표적인 Data Structure를 정리해보려 합니다. 42 Seoul에서 PushSwap Project를 구현할 때가 새록새록 나더군요,, 일단 Stack에 대해 알아봅시다 1. 스택이란? 우리가 흔히 일반적으
songye.tistory.com
Data Structure, Queue
지난 시간에는 Stack 자료 구조에 대해 알아봤습니다! https://songye.tistory.com/18 Data Structure, Stack Stack과 Queue, Hash Table 등 대표적인 Data Structure를 정리해보려 합니다. 42 Seoul에서 PushSwap Project를 구현할
songye.tistory.com
1. 서론
근데 지금 구현한 Stack이랑 Queue는 String 타입밖에 못쓰는거 아니에여?
다른 타입으로 쓸 때 매번 만들어야하면 귀찮잖아요 그쵸? (제가 귗낳습ㄴ디)
그러면 그 때 그 때 타입 캐스팅해서 쓰면 되는거 아닌가요?
타입 캐스팅을 남발하면 문제가 생깁니다.
가장 큰 문제는 Runtime Error에서 적발된다.
이게 무슨 소리냐 디버깅 비용(시간)이 많이 든다는 겁니다 (이게 무슨 소리람)
프로그래밍을 하다보면 우리가 흔히 두 가지 에러를 만나게 됩니다.
Runtime Error와 Compile Error 이 두 가지 에러를 세상에서 제일 많이 만날겁니다.
(1) - 1 Compile Error
말 그대로 Compile Error는 IDE 혹은 터미널 환경에서 Compiler를 통해
실행 전에 Error를 알려주기 때문에 비교적 Error를 쉽게 찾을 수 있습니다.
(1) - 2 Run time error
Run Time Error는 말 그대로 실행 중 발생한 Error 이기에 코드를 실행해야만 Error를 찾을 수 있으며
디버깅하는 과정이 Compile Error보다 상대적으로 복잡하고 이유를 찾기 어려울 수 있습니다.
Type Casting와 관련된 에러는 Compile Error가 아닌 Runtime Error에서 발생하기 때문에
Type Casting은 지양하는 것이 좋습니다.
2. Generic Type
챗 GPT한테 한 번 정의를 물어봅시다
제네릭 타입(Generic Type)은 프로그래밍 언어에서 타입이나 함수를 선언할 때, 그 타입이나 함수를 실제로 사용될 때까지 타입을 명시하지 않고 나중에 지정할 수 있는 기능입니다. 제네릭을 사용하면 코드의 재사용성을 높이고, 타입 안정성을 유지하면서 유연성을 확보할 수 있습니다.
Generic Type의 장점
(1) 코드 재사용성
GPT 친구가 말해준 것처럼 재사용성을 굉장히 높여줍니다. 일일이 구현해놓은 함수나 타입을 재정의하거나 타입 캐스팅할 필요가 없는거죠
(2) 타입 안정성
위에서 언급한 TypeCasting시 Run Time Error를 Generic Type을 사용시에는 Compile Error로 잡을 수 있습니다.
지금에야 간단한 코드를 작성하기 때문에 문제가 커보이지 않지만 규모가 큰 프로젝트를 진행할 때 문제가 발생한다면 끔찍합니다
3. Generic Stack 만들어보기
(1) Linked List Generic Stack
백문이 불여일견, 백 번 설명보다 한 번 직접 구현해보는 것이 빠릅니다.
import java.util.NoSuchElementException;
public class Stack<Item> {
private Node first = null;
private class Node {
Node next;
Item item;
}
public void Push(Item item) {
Node second = this.first;
this.first = new Node();
this.first.next = second;
this.first.item = item;
}
public Item Pop() {
if (this.IsEmpty()) {
throw new NoSuchElementException("스택이 비어있습니다.");
} else {
Item item = this.first.item;
this.first = this.first.next;
return item;
}
}
public boolean IsEmpty() {
return this.first == null;
}
}
기존 Linked List로 구현한 Stack에서 두 가지만 바뀌었습니다.
1. class Stack<Item>
Stack Class에서 타입이 정해지지 않은 Item이라는 Generic Type을 사용하겠다 선언하는 부분입니다.
2. String -> Item
기존 String으로 선언된 변수들의 Type을 새로 지정되지 않은 Item이라는 Generic Type으로 변경하면 끝!
main문에서 한번 실행시켜 결과도 한 번 확인해봅시다.
import java.util.NoSuchElementException;
public class Main {
public static void main(String[] args) {
Stack<String> stack = new Stack<String>();
try {
stack.Push("First Item");
String item = stack.Pop();
System.out.println(item);
stack.Pop();
} catch (NoSuchElementException var6) {
System.out.println("스택이 비어있습니다. 에러 메시지: " + var6.getMessage());
}
}
}
간단한 구현을 통해 코드의 재사용성과 안정성을 줄 수 있습니다.
이번에는 실제 Compile Error가 검출되는지 확인해봅시다.
Generic Stack을 Integer Type으로 인스턴스화 한 후에 String Data를 Push할 경우
Compile Error가 발생하는 것을 확인할 수 있습니다.
(2) Array Generic Stack
import java.util.NoSuchElementException;
public class Stack<Item> {
int N = 0;
Item[] arr = (Item[]) new Object[4];
public Stack() {
}
public void Push(Item item) {
if (this.N > this.arr.length - 1) {
this.DupArr(this.arr.length * 2);
}
this.arr[this.N++] = item;
}
public Item Pop() {
if (this.IsEmpty()) {
throw new NoSuchElementException("스택이 비어있습니다.");
} else {
Item item = this.arr[this.N - 1];
this.arr[this.N - 1] = null;
--this.N;
if (this.N < this.arr.length / 4) {
this.DupArr(this.arr.length / 2);
}
return item;
}
}
public boolean IsEmpty() {
return this.N == 0;
}
public void DupArr(int size) {
Item[] temp = (Item[]) new Object[size];
for(int i = 0; i < this.N; ++i) {
temp[i] = this.arr[i];
}
this.arr = temp;
}
}
어? 근데 TypeCasting 쓰지 말라믄서요,,!
죄송합니다,, Array로 Generic 구현할 때는 불가피하게 Object Class 객체를 통해서 만들어야 하기 때문에 사용해야합니다,,
결과는 앞서 봤기 때문에 생략하겠습니다!
Generic Type으로 만들 때 Array로 쓰는게 조금 더 귀찮습니다 ㅎ
클래스 변수 선언부 따로 만들고 생성자에서 값 넣어야하는데 저것도 습관이네요
큐는 귀찮으니 넘어가겠습니다 Stack이랑 방식은 똑같기 때문에
다음에는 Stack과 Queue Application을 한 번 살펴보죠