Java
[Java] 컬렉션과 제네릭
dalooong
2023. 5. 30. 14:41
컬렉션과 제네릭
1. 컬렉션
1-1. 컬렉션이란?
컬렉션 프레임워크는 자바에서 데이터를 저장, 관리 및 조작하는 데 사용되는 API(응용 프로그래밍 인터페이스) 집합입니다. 이 프레임워크는 데이터 구조의 구현과 관련된 클래스와 인터페이스를 제공하여 데이터를 효율적으로 저장하고 조작할 수 있도록 도와줍니다.
1-2. 컬렉션과 배열의 차이점
컬렉션과 배열은 모두 여러 개의 요소를 저장하는 데 사용되지만, 다음과 같은 차이점이 있습니다.
- 크기: 배열은 고정된 크기를 가지지만, 컬렉션은 동적으로 크기가 조정될 수 있습니다. 컬렉션은 요소를 추가하거나 제거함으로써 크기를 조절할 수 있습니다.
- 타입: 배열은 동일한 데이터 타입의 요소만 저장할 수 있지만, 컬렉션은 다양한 데이터 타입의 요소를 저장할 수 있습니다. 제네릭을 사용하여 컬렉션에 저장되는 요소의 타입을 지정할 수도 있습니다.
- 유연성: 배열은 요소에 직접 접근할 수 있어 빠른 접근이 가능하지만, 컬렉션은 보다 다양한 조작을 지원하며 편리한 기능을 제공합니다. 예를 들어, 컬렉션은 검색, 정렬, 필터링, 반복 등 다양한 연산을 지원합니다.
1-3. 컬렉션 인터페이스에는 어떤것들이 있을까요?
- Collection 인터페이스: 컬렉션의 일반적인 동작을 정의합니다. 다른 컬렉션 인터페이스의 부모 인터페이스로 사용됩니다.
- List 인터페이스: 순서가 있는 컬렉션을 정의하며, 중복된 요소를 허용합니다. 요소에 대한 인덱스를 사용하여 접근할 수 있습니다.
- Set 인터페이스: 중복된 요소를 허용하지 않는 순서가 없는 컬렉션을 정의합니다. 유일한 요소만 포함할 수 있습니다.
- Queue 인터페이스: 요소를 추가하거나 제거할 때 특정 순서를 따르는 컬렉션을 정의합니다. 일반적으로 FIFO(First-In-First-Out) 순서를 따릅니다.
- Map 인터페이스: 키와 값으로 구성된 요소의 집합을 정의합니다. 각 요소는 유일한 키를 가지며, 키를 사용하여 값을 검색하고 조작할 수 있습니다.
- Stack 인터페이스: 요소를 추가하거나 제거할 때 특정 순서를 따르는 컬렉션을 정의합니다. 기본적으로 LIFO(Last in First Out) 순서를 따릅니다.
1-4. 컬렉션 구현
List 인터페이스
- 특징: 순서가 있는 컬렉션으로, 중복된 요소를 허용합니다. 인덱스를 사용하여 요소에 접근할 수 있습니다.
- 사용법: List를 구현한 클래스로 ArrayList, LinkedList 등이 있습니다. add, get, remove 등의 메서드를 사용하여 요소를 추가, 조회, 삭제할 수 있습니다.
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
System.out.println(names.get(0)); // Alice
System.out.println(names.size()); // 3
names.remove("Bob");
System.out.println(names); // [Alice, Charlie]
Set 인터페이스
- 특징: 중복된 요소를 허용하지 않는 순서가 없는 컬렉션입니다. 유일한 요소만을 포함합니다.
- 사용법: Set을 구현한 클래스로 HashSet, TreeSet 등이 있습니다. add, contains, remove 등의 메서드를 사용하여 요소를 추가, 포함 여부 확인, 삭제할 수 있습니다.
Set<String> fruits = new HashSet<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Apple"); // 중복된 요소이므로 추가되지 않음
System.out.println(fruits.contains("Banana")); // true
System.out.println(fruits.size()); // 2
fruits.remove("Apple");
System.out.println(fruits); // [Banana]
Map 인터페이스
- 특징: 키와 값으로 구성된 요소의 집합입니다. 각 요소는 유일한 키를 가지며, 키를 사용하여 값을 검색하고 조작할 수 있습니다.
- 사용법: Map을 구현한 클래스로 HashMap, TreeMap 등이 있습니다. put, get, remove 등의 메서드를 사용하여 키-값 쌍을 추가, 조회, 삭제할 수 있습니다.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 80);
scores.put("Bob", 90);
scores.put("Charlie", 75);
System.out.println(scores.get("Bob")); // 90
System.out.println(scores.containsKey("Charlie")); // true
scores.remove("Alice");
System.out.println(scores); // {Bob=90, Charlie=75}
Queue 인터페이스
- 특징: 요소를 추가하거나 제거할 때 특정 순서를 따르는 컬렉션을 정의합니다. 일반적으로 FIFO(First-In-First-Out) 순서를 따릅니다.
- 사용법: Queue를 구현한 클래스로 LinkedList, PriorityQueue 등이 있습니다. offer, poll, peek 등의 메서드를 사용하여 요소를 추가, 제거, 조회할 수 있습니다.
Queue<String> queue = new LinkedList<>();
queue.offer("A");
queue.offer("B");
queue.offer("C");
System.out.println(queue.poll()); // A
System.out.println(queue.peek()); // B
System.out.println(queue.size()); // 2
Stack 인터페이스
- 특징: 요소를 추가하거나 제거할 때 LIFO(Last-In-First-Out) 순서를 따르는 컬렉션을 정의합니다.
- 사용법: Stack을 구현한 클래스로 Stack 클래스가 있습니다. push, pop, peek 등의 메서드를 사용하여 요소를 추가, 제거, 조회할 수 있습니다.
번외: Stack VS Queue
Queue 인터페이스는 주로 작업 대기열, 이벤트 처리 등에서 사용되며, Stack 인터페이스는 주로 재귀 알고리즘, 브라우저의 뒤로 가기 기능 등에서 사용됩니다. 각 인터페이스는 요소의 추가, 제거, 조회를 위한 메서드를 제공하여 데이터를 효율적으로 관리할 수 있습니다.
2. 제네릭
2-1. 제네릭이란?
제네릭은 자바에서 컴파일 시점에서 타입을 안전하게 지정하기 위한 기능입니다. 제네릭을 사용하면 클래스나 메서드를 선언할 때 타입 매개변수를 사용하여 일반화된 타입을 지정할 수 있습니다.
장점
- 타입 안정성: 컴파일 시점에서 타입 체크를 수행하여 런타임 오류를 방지합니다. 잘못된 타입으로 인한 예외나 에러를 미리 방지할 수 있습니다.
- 재사용성: 일반화된 타입을 사용하여 여러 종류의 객체를 처리할 수 있습니다. 제네릭을 활용하면 코드의 재사용성을 높일 수 있습니다.
- 가독성: 제네릭을 사용하면 코드의 의도가 명확해지고 가독성이 좋아집니다. 타입 정보가 명시되기 때문에 코드를 이해하기 쉽습니다.
2-2. 제네릭 타입 활용 방법
제네릭 타입:
클래스 또는 인터페이스를 선언할 때 타입 매개변수를 사용하여 일반화된 타입을 지정합니다. 제네릭 타입을 선언하면 객체 생성 시에 타입을 구체화하여 사용할 수 있습니다.
public class Box<T> {
private T item;
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
Box<Integer> intBox = new Box<>();
intBox.setItem(10);
int value = intBox.getItem(); // 형변환 없이 정수로 사용 가능
2-3. 제네릭 메서드 활용방법
제네릭 메서드:
메서드를 선언할 때 타입 매개변수를 사용하여 메서드의 매개변수나 반환값의 타입을 지정합니다. 제네릭 메서드를 사용하면 메서드마다 타입을 지정하여 다양한 타입의 인자와 반환값을 처리할 수 있습니다.
public <T> T getFirstElement(List<T> list) {
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
String firstElement = getFirstElement(stringList); // "Hello"