계산기 과제 레벨3 하려니, 스트림, 람다식에 대해 잘 몰라서 다시 복습 진행하였다.
스트림(Stream)이란?
✔ “데이터의 흐름”을 추상화한 개념으로, 데이터를 효율적으로 처리할 수 있도록 도와주는 API.
✔ 배열, 컬렉션(List, Set, Map 등)에서 데이터를 다룰 때 for문 없이 간결한 코드로 처리 가능.
✔ 데이터를 필터링, 변환, 정렬, 그룹화, 집계 등의 연산을 “함수형 프로그래밍 방식”으로 적용 가능.
스트림 적용 예
스트림 미적용시(for문 사용)
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
names.add("Andrew");
List<String> filteredNames = new ArrayList<>();
for (String name : names) {
if (name.startsWith("A")) {
filteredNames.add(name.toUpperCase()); // A로 시작하는 이름을 대문자로 변환
}
}
for (String name : filteredNames) {
System.out.println(name);
}
}
}
ALICE
ANDREW
스트림 방식
📌 names.stream()을 호출하면 **“names 리스트에 저장된 데이터를 순차적으로 처리할 준비가 된 흐름(Stream)”**을 반환함.
📌 이후 .filter(), .map(), .forEach() 등을 사용하여 데이터를 가공할 수 있음.
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
// List<String> names : 데이터를 저장하는 컬렉션 (즉, 요소를 담고 있는 컨테이너).
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Andrew");
// .asList 함수
// 일반 배열을 ArrayList로 변환합니다
// 고정된 배열을 반환합니다
// 읽기 전용 배열을 반환합니다
names.stream() // ✅ 데이터 저장이 아니라, 데이터를 “흐름”으로 처리할 수 있는 스트림을 생성
.filter(name -> name.startsWith("A")) // ✅ A로 시작하는 이름 필터링
.map(String::toUpperCase) // ✅ 대문자로 변환
.forEach(System.out::println); // ✅ 최종 출력
}
}
ALICE
ANDREW
스트림 주요 기능
스트림 메서드 | 설명 | 예제 |
.filter() | 조건에 맞는 데이터만 걸러냄 | stream.filter(n -> n > 10) |
.map() | 데이터를 변환 | stream.map(String::toUpperCase) |
sorted() | 정렬 수행 | stream.sorted() |
.distinct() | 중복 제거 | stream.distinct() |
.limit(n) | 처음 n개 요소만 가져옴 | stream.limit(3) |
.skip(n) | 처음 n개 요소를 건너뜀 | stream.skip(2) |
.count() | 개수 반환 | stream.count() |
.forEach() | 최종 출력 | stream.forEach(System.out::println) |
.collect() | 최종 연산, 데이터를 List, Set, Map등의 자료구조로 변환 | stream.collect(Collectors.toList()) |
스트림 동작 원리
✔ 스트림은 “중간 연산”과 “최종 연산”으로 구성됨
✔ 중간 연산(filter(), map(), sorted())은 데이터를 변환하지만 실제 실행되지 않음
✔ 최종 연산(forEach(), count(), collect())을 호출해야만 스트림이 실행됨!
예제 : 스트림 동작 과정
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "cherry", "avocado");
long count = words.stream()
.filter(word -> {
System.out.println("Filtering: " + word);
return word.startsWith("a");
})
.map(word -> {
System.out.println("Mapping: " + word); // System.out.println()은 변환 전 값
return word.toUpperCase();
})
.count();
System.out.println("결과 개수: " + count);
}
}
Filtering: apple
Mapping: apple
Filtering: banana
Filtering: cherry
Filtering: avocado
Mapping: avocado
결과 개수: 2
🔍 실행 과정
단계 | 실행 내용 |
Filtering: apple | "apple".startWith("a") == true (통과) |
Mapping: apple | "apple"이 APPLE로 변환됨 |
Filtering: banana | "banana".startWith("a") == false(제외) |
Filtering: cherry | "banana".startWith("a") == false(제외) |
Filtering: avocado | "apple".startWith("a") == true (통과) |
Mapping: avocado | "avocado"가 AVOCADO로 변환됨 |
count() | 최종적으로 2개가 남아서 개수 출력 |
🔍 return word.startsWith("a"); 코드
입력 값 | word.startsWith("a") 결과 | filter()에서 남음? |
"apple" | ✅ true | ✅ 남음 |
"banana" | ❌ false | ❌ 제외 |
"cherry" | ❌ false | ❌ 제외 |
"avocado" | ✅ true | ✅ 남음 |
스트림과 컬렉션의 차이
구분 | 스트림(Stream) | 컬렉션(Collection) |
데이터 저장 여부 | ❌ 데이터를 저장하지 않음 | ✅ 데이터를 저장함 |
요소 처리 방식 | ✅ 데이터를 한번만 처리 (1회성) | ✅ 여러 번 반복 가능 |
연산 방식 | ✅ 함수형 프로그래밍 스타일 (filter(), map()) | ✅ for문을 사용한 명령형 프로그래밍 |
병렬 처리 | ✅ 가능 (parallelStream()) | ❌ 직접 for문으로 병렬화 필요 |
* 대량 데이터를 다룰 때 parallelStream()을 사용하면 자동으로 병렬 처리 가능
parallelStream()은 스트림을 병렬(Parallel)로 처리하는 기능을 제공하는 메서드로
여러 개의 CPU 코어를 활용해서 “여러 데이터 조각을 동시에 처리”할 수 있음
반면 stream()은 데이터를 “순차적(Sequential)“으로 처리함.
stream() vs parallelStream() 비교
구분 | stream() | parallelStream() |
실행 방식 | 한 개의 스레드에서 차례로 실행 | 여러 개의 스레드에서 동시에 실행 |
성능 | 데이터가 적으면 빠름 | 데이터가 많을수록 성능 향상 |
순서 보장 | 순서 유지 O | 순서 보장 X |
CPU 활용 | 단일 CPU 코어만 사용 | 멀티 코어 CPU 활용 (병렬 연산) |
사용 예시 | 작은 데이터 처리 | 대량의 데이터 처리, 계산량이 많은 작업 |
stream() 예제 (순차 처리)
import java.util.Arrays;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
names.stream()
.map(name -> {
System.out.println("Mapping: " + name + " - " + Thread.currentThread().getName());
return name.toUpperCase();
})
.forEach(System.out::println);
}
}
Mapping: Alice - main
Mapping: Bob - main
Mapping: Charlie - main
Mapping: David - main
ALICE
BOB
CHARLIE
DAVID
parallelStream() 사용 예
import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
names.parallelStream()
.map(name -> {
System.out.println("Mapping: " + name + " - " + Thread.currentThread().getName());
return name.toUpperCase();
})
.forEach(System.out::println);
}
}
// 실행할 때마다 순서가 다를 수 있음
Mapping: Alice - ForkJoinPool.commonPool-worker-1
Mapping: Bob - main
Mapping: Charlie - ForkJoinPool.commonPool-worker-2
Mapping: David - ForkJoinPool.commonPool-worker-1
BOB
CHARLIE
DAVID
ALICE
성능 테스트 (연산 속도 측정)
1부터 1억까지의 숫자를 더하는 코드 비교 (stream() vs parallelStream())
import java.util.stream.LongStream;
public class StreamPerformanceTest {
public static void main(String[] args) {
long start, end;
// ✅ 순차 처리 (stream)
start = System.currentTimeMillis();
long sum1 = LongStream.rangeClosed(1, 100_000_000).sum();
end = System.currentTimeMillis();
System.out.println("순차 처리 결과: " + sum1 + " (걸린 시간: " + (end - start) + "ms)");
// ✅ 병렬 처리 (parallelStream)
start = System.currentTimeMillis();
long sum2 = LongStream.rangeClosed(1, 100_000_000).parallel().sum();
end = System.currentTimeMillis();
System.out.println("병렬 처리 결과: " + sum2 + " (걸린 시간: " + (end - start) + "ms)");
}
}
// CPU 성능에 따라 다를 수 있음
순차 처리 결과: 5000000050000000 (걸린 시간: 500ms)
병렬 처리 결과: 5000000050000000 (걸린 시간: 200ms)
람다식(Lambda Expression)
✔ “메서드를 하나의 식(Expression)으로 간단히 표현하는 방법”
✔ 불필요한 class 선언 없이, 코드가 짧고 가독성이 좋아짐
✔ 함수형 프로그래밍을 자바에서 사용할 수 있도록 도입된 기능
람다식 적용 예
람다식 미적용 예
interface Calculator {
int sum(int a, int b);
}
public class Main {
public static void main(String[] args) {
Calculator cal = new Calculator() { // ✅ 익명 클래스 사용
@Override
public int sum(int a, int b) {
return a + b;
}
};
System.out.println(cal.sum(5, 3)); // 출력: 8
}
}
람다식 적용 예
interface Calculator {
int sum(int a, int b);
}
public class Main {
public static void main(String[] args) {
Calculator cal = (a, b) -> a + b; // ✅ 람다식 사용!
System.out.println(cal.sum(5, 3)); // 출력: 8
}
}
🐧 람다식 기본 구조
(매개변수) -> { 실행문 }
예제 1: 매개변수가 하나인 경우
str -> System.out.println(str);
예제 2: 매개변수가 여러 개인 경우
(a, b) -> a + b;
예제 3: 실행문이 여러 개면 {} 사용
(a, b) -> {
int sum = a + b;
return sum;
};
예제 4: 매개변수가 없는 경우
interface Printer {
void print();
}
public class Main {
public static void main(String[] args) {
Printer printer = () -> System.out.println("Hello, Lambda!");
printer.print();
}
}
예제 5: 람다식을 활용한 스트림(Stream) 예제
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.filter(name -> name.startsWith("A")) // ✅ A로 시작하는 이름 필터링
.map(String::toUpperCase) // ✅ 대문자로 변환
.forEach(System.out::println); // ✅ 출력 //ALICE
}
}
'코딩 > sparta TIL' 카테고리의 다른 글
CH 2 : 키오스크 과제 (0) | 2025.03.10 |
---|---|
TIL 14 : StringBuilder (0) | 2025.03.10 |
TIL 11 : 계산기 과제 V2, Markdown 문법, Enum (0) | 2025.03.04 |
TIL 10 : 나태지옥, 백준 4단계(1차원 배열), 컬렉션 공부 (0) | 2025.02.28 |
TIL 9 : BufferedReader, BufferedWriter (0) | 2025.02.27 |