챕터 8: 스트림과 컬렉션 프레임워크
Java의 스트림(Stream) API와 컬렉션 프레임워크는 데이터를 효율적으로 처리하고 관리하기 위한 강력한 도구입니다. 이 챕터에서는 스트림과 컬렉션 프레임워크의 기본 개념, 주요 기능, 그리고 이를 활용한 예제 코드를 자세히 설명합니다.
8.1 스트림 API
스트림 API는 Java 8에서 도입된 기능으로, 컬렉션 데이터를 간결하고 선언적으로 처리할 수 있게 해줍니다. 스트림은 컬렉션의 데이터 요소들을 순차적으로 처리하는 파이프라인을 제공합니다.
8.1.1 스트림 생성
스트림은 다양한 소스(컬렉션, 배열, I/O 채널 등)에서 생성할 수 있습니다.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamCreationExample {
public static void main(String[] args) {
// 리스트로부터 스트림 생성
List<String> list = Arrays.asList("A", "B", "C");
Stream<String> listStream = list.stream();
// 배열로부터 스트림 생성
String[] array = {"X", "Y", "Z"};
Stream<String> arrayStream = Arrays.stream(array);
// 직접 스트림 생성
Stream<String> directStream = Stream.of("1", "2", "3");
// 출력 예제
listStream.forEach(System.out::println); // 출력: A B C
arrayStream.forEach(System.out::println); // 출력: X Y Z
directStream.forEach(System.out::println); // 출력: 1 2 3
}
}
설명: 스트림은 컬렉션, 배열, 직접 생성 등을 통해 쉽게 만들 수 있습니다. 스트림을 생성한 후 forEach 메서드를 사용하여 각 요소를 출력할 수 있습니다.
8.1.2 스트림 연산
스트림 연산은 중간 연산(Intermediate Operations)과 최종 연산(Terminal Operations)으로 나뉩니다.
- 중간 연산: 스트림을 반환하며, 체이닝이 가능합니다. 예: filter(), map(), sorted()
- 최종 연산: 스트림을 닫고 결과를 반환합니다. 예: forEach(), collect(), reduce()
8.1.3 스트림 파이프라인 예제
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamPipelineExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// 스트림 파이프라인: 필터링, 매핑, 수집
List<String> result = list.stream()
.filter(fruit -> fruit.startsWith("a") || fruit.startsWith("e")) // 'a' 또는 'e'로 시작하는 요소 필터링
.map(String::toUpperCase) // 각 요소를 대문자로 변환
.sorted() // 정렬
.collect(Collectors.toList()); // 리스트로 수집
// 결과 출력
result.forEach(System.out::println); // 출력: APPLE ELDERBERRY
}
}
설명: 위 예제에서 스트림은 filter를 통해 특정 조건에 맞는 요소를 필터링하고, map을 통해 대문자로 변환하며, sorted를 통해 정렬한 후, collect를 통해 리스트로 수집합니다.
8.1.4 스트림의 병렬 처리
스트림은 병렬 처리 기능을 제공하여 성능을 향상시킬 수 있습니다.
import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// 병렬 스트림 생성 및 처리
list.parallelStream()
.filter(fruit -> fruit.length() > 5) // 길이가 5보다 큰 요소 필터링
.forEach(System.out::println); // 병렬 처리로 각 요소 출력
}
}
설명: parallelStream 메서드를 사용하여 병렬 스트림을 생성할 수 있습니다. 병렬 스트림은 내부적으로 여러 스레드를 사용하여 데이터를 병렬로 처리합니다. 병렬 처리를 통해 대규모 데이터를 더 빠르게 처리할 수 있습니다.
8.1.5 스트림 연산의 종류
스트림 연산은 크게 두 가지로 나눌 수 있습니다.
- 중간 연산 (Intermediate Operations): 스트림을 변환하여 새로운 스트림을 반환합니다. 중간 연산은 게으르게(lazy) 평가되며, 최종 연산이 호출될 때까지 실제로 수행되지 않습니다.
- filter(Predicate<? super T> predicate): 주어진 조건을 만족하는 요소만 포함하는 스트림을 반환합니다.
- map(Function<? super T, ? extends R> mapper): 각 요소를 주어진 함수에 적용한 결과로 변환한 스트림을 반환합니다.
- sorted(): 자연 순서로 정렬된 스트림을 반환합니다.
- distinct(): 중복을 제거한 스트림을 반환합니다.
- limit(long maxSize): 주어진 길이 이하의 스트림을 반환합니다.
- skip(long n): 처음 n개의 요소를 건너뛴 스트림을 반환합니다.
- 최종 연산 (Terminal Operations): 스트림을 닫고 결과를 반환합니다. 최종 연산이 호출되면 스트림의 요소들이 소비되고, 이후에는 더 이상 사용할 수 없습니다.
- forEach(Consumer<? super T> action): 각 요소에 대해 주어진 동작을 수행합니다.
- collect(Collector<? super T, A, R> collector): 스트림의 요소들을 수집하여 결과를 반환합니다.
- reduce(BinaryOperator<T> accumulator): 스트림의 요소들을 반복적으로 결합하여 결과를 반환합니다.
- count(): 스트림의 요소 수를 반환합니다.
- anyMatch(Predicate<? super T> predicate): 주어진 조건을 만족하는 요소가 있는지 여부를 반환합니다.
8.2 컬렉션 프레임워크
Java의 컬렉션 프레임워크는 데이터를 저장하고 조작하기 위한 인터페이스와 클래스의 집합입니다. 주요 인터페이스로는 List, Set, Map 등이 있으며, 이를 구현한 다양한 클래스가 있습니다.
8.2.1 List 인터페이스
List는 순서가 있는 요소의 집합으로, 중복을 허용합니다.
- ArrayList 예제
import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 요소 순회 및 출력
for (String fruit : list) {
System.out.println(fruit);
}
// 특정 요소에 접근
System.out.println("First element: " + list.get(0)); // 출력: First element: Apple
}
}
설명: ArrayList는 동적 배열로 구현된 List의 한 종류입니다. 요소를 추가, 제거, 접근하는 기능을 제공합니다.
- LinkedList 예제
import java.util.LinkedList;
import java.util.List;
public class LinkedListExample {
public static void main(String[] args) {
List<String> list = new LinkedList<>();
list.add("Dog");
list.add("Cat");
list.add("Cow");
// 요소 순회 및 출력
for (String animal : list) {
System.out.println(animal);
}
// 첫 번째 요소 제거
list.remove(0);
System.out.println("After removal: " + list); // 출력: After removal: [Cat, Cow]
}
}
설명: LinkedList는 노드로 연결된 리스트로 구현된 List의 한 종류입니다. 요소의 삽입, 삭제가 빠른 것이 특징입니다. LinkedList는 리스트의 처음과 끝에서 요소를 삽입하고 삭제하는 데 특히 유리합니다.
8.2.2 Set 인터페이스
Set은 중복을 허용하지 않는 요소의 집합입니다.
- HashSet 예제
import java.util.HashSet;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Dog");
set.add("Cat");
set.add("Bird");
set.add("Cat"); // 중복된 요소는 추가되지 않음
// 요소 순회 및 출력
for (String animal : set) {
System.out.println(animal);
}
// 특정 요소 포함 여부 확인
System.out.println("Set contains 'Dog': " + set.contains("Dog")); // 출력: Set contains 'Dog': true
}
}
설명: HashSet은 해시 테이블로 구현된 Set의 한 종류로, 중복된 요소를 허용하지 않습니다. 요소를 추가, 제거, 포함 여부를 확인할 수 있습니다.
- TreeSet 예제
import java.util.Set;
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("Dog");
set.add("Cat");
set.add("Bird");
// 요소 순회 및 출력 (정렬된 순서)
for (String animal : set) {
System.out.println(animal);
}
// 특정 요소 포함 여부 확인
System.out.println("Set contains 'Dog': " + set.contains("Dog")); // 출력: Set contains 'Dog': true
}
}
설명: TreeSet은 이진 트리로 구현된 Set의 한 종류로, 요소를 정렬된 순서로 저장합니다. 요소를 추가, 제거, 포함 여부를 확인할 수 있습니다.
8.2.3 Map 인터페이스
Map은 키와 값의 쌍으로 이루어진 요소의 집합입니다.
- HashMap 예제
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 1);
map.put("Banana", 2);
map.put("Cherry", 3);
// 특정 키의 값 가져오기
System.out.println("Value for 'Apple': " + map.get("Apple")); // 출력: Value for 'Apple': 1
// 키와 값 순회 및 출력
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
설명: HashMap은 해시 테이블로 구현된 Map의 한 종류로, 키와 값의 쌍을 저장합니다. 키는 중복될 수 없으며, 각 키는 하나의 값에 매핑됩니다.
- TreeMap 예제
import java.util.Map;
import java.util.TreeMap;
public class TreeMapExample {
public static void main(String[] args) {
Map<String, Integer> map = new TreeMap<>();
map.put("Apple", 1);
map.put("Banana", 2);
map.put("Cherry", 3);
// 특정 키의 값 가져오기
System.out.println("Value for 'Apple': " + map.get("Apple")); // 출력: Value for 'Apple': 1
// 키와 값 순회 및 출력 (정렬된 순서)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
설명: TreeMap은 이진 트리로 구현된 Map의 한 종류로, 키를 정렬된 순서로 저장합니다. 키와 값의 쌍을 저장하며, 키는 중복될 수 없습니다. TreeMap은 키를 기준으로 자연 순서 또는 사용자 정의 순서로 정렬합니다.
8.3 컬렉션 유틸리티 클래스
Java는 컬렉션을 조작하기 위한 유틸리티 클래스를 제공합니다. 주요 클래스로는 Collections와 Arrays가 있습니다.
8.3.1 Collections 클래스
Collections 클래스는 컬렉션 조작을 위한 다양한 메서드를 제공합니다.
- Collections 예제
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 3, 1, 4, 1, 5, 9);
// 리스트 정렬
Collections.sort(list);
System.out.println("Sorted list: " + list); // 출력: Sorted list: [1, 1, 3, 4, 5, 9]
// 요소 섞기
Collections.shuffle(list);
System.out.println("Shuffled list: " + list);
// 최대, 최소값 찾기
int max = Collections.max(list);
int min = Collections.min(list);
System.out.println("Max: " + max + ", Min: " + min); // 출력: Max: 9, Min: 1
}
}
설명: Collections 클래스는 리스트를 정렬, 섞기, 최대/최소값 찾기 등 다양한 조작을 할 수 있는 메서드를 제공합니다. sort 메서드는 리스트를 오름차순으로 정렬하고, shuffle 메서드는 리스트의 요소들을 무작위로 섞습니다. max와 min 메서드는 리스트에서 최대값과 최소값을 찾습니다.
8.3.2 Arrays 클래스
Arrays 클래스는 배열 조작을 위한 다양한 메서드를 제공합니다.
- Arrays 예제
import java.util.Arrays;
public class ArraysExample {
public static void main(String[] args) {
int[] array = {3, 1, 4, 1, 5, 9};
// 배열 정렬
Arrays.sort(array);
System.out.println("Sorted array: " + Arrays.toString(array)); // 출력: Sorted array: [1, 1, 3, 4, 5, 9]
// 배열 복사
int[] copiedArray = Arrays.copyOf(array, array.length);
System.out.println("Copied array: " + Arrays.toString(copiedArray));
// 배열 채우기
Arrays.fill(array, 0);
System.out.println("Filled array: " + Arrays.toString(array)); // 출력: Filled array: [0, 0, 0, 0, 0, 0]
}
}
설명: Arrays 클래스는 배열을 정렬, 복사, 채우기 등 다양한 조작을 할 수 있는 메서드를 제공합니다. sort 메서드는 배열을 오름차순으로 정렬하고, copyOf 메서드는 배열을 복사하며, fill 메서드는 배열의 모든 요소를 지정된 값으로 채웁니다.
이로써 Java의 스트림과 컬렉션 프레임워크에 대해 자세히 설명하고, 각 개념을 코드와 예시를 통해 설명했습니다. 다음 챕터에서는 Java의 파일 I/O 및 네트워킹에 대해 다루겠습니다.
'IT 강좌(IT Lectures) > Java' 카테고리의 다른 글
10강. 기초 프로젝트 (0) | 2024.06.27 |
---|---|
9강. 파일 I/O 및 네트워킹 (0) | 2024.06.26 |
7강. 동시성 프로그래밍 (0) | 2024.06.24 |
6강. 고급 객체 지향 기법 (0) | 2024.06.23 |
5강. Java API 활용 (0) | 2024.06.22 |