필터 만들기
package lambda.lambda5.filter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class FilterMainV2 {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 짝수만 거르기
List<Integer> evenNumbers = filter(numbers, i-> i%2 == 0);
System.out.println(evenNumbers);
// 홀수만 거르기
List<Integer> oddNumbers = filter(numbers, i-> i%2 != 0);
System.out.println(oddNumbers);
}
private static List<Integer> filter(List<Integer> numbers, Predicate<Integer> predicate) {
ArrayList<Integer> filtered = new ArrayList<>();
for(Integer num : numbers) {
if(predicate.test(num)) {
filtered.add(num);
}
}
return filtered;
}
}
실행 결과

제네릭을 사용한 필터 유틸 클래스 생성
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
List<T> filtered = new ArrayList<>();
for(T num : list) {
if(predicate.test(num)) {
filtered.add(num);
}
}
return filtered;
}
맵 만들기
package lambda.lambda5.map;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class MapMainV2 {
public static void main(String[] args) {
List<String> list = List.of("1", "12", "123", "1234");
// 문자를 숫자로 변환
List<Integer> numbers = map(list, s -> Integer.valueOf(s));
System.out.println(numbers);
// 문자열의 길이 반환
List<Integer> lengths = map(list, s -> s.length());
System.out.println(lengths);
}
private static List<Integer> map(List<String> list, Function<String, Integer> mapper) {
List<Integer> numbers = new ArrayList<>();
for(String s : list) {
numbers.add(mapper.apply(s));
}
return numbers;
}
}
실행 결과

제네릭을 활용한 맵 유틸 클래스
public static <T, R> List<R> map(List<T> list, Function<T, R> mapper) {
List<R> numbers = new ArrayList<>();
for(T s : list) {
numbers.add(mapper.apply(s));
}
return numbers;
}
스트림 만들기
스트림은 자신의 데이터를 "필터" 하거나, "매핑" 하여 새로운 스트림을 만들 수 있다.
MyStreamV1.java
package lambda.lambda5.mystream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public class MyStreamV1 {
private List<Integer> internalList;
public MyStreamV1(List<Integer> internalList) {
this.internalList = internalList;
}
public MyStreamV1 filter(Predicate<Integer> predicate) {
List<Integer> filtered = new ArrayList<>();
for(Integer i : internalList) {
if(predicate.test(i)) {
filtered.add(i);
}
}
return new MyStreamV1(filtered);
}
public MyStreamV1 map(Function<Integer, Integer> mapper) {
List<Integer> mapped = new ArrayList<>();
for(Integer element : internalList) {
mapped.add(mapper.apply(element));
}
return new MyStreamV1(mapped);
}
public List<Integer> toList() {
return internalList;
}
}
MyStreamV1Main.java
리스트에서 짝수만 남기고, 이를 2배 후 반환하는 스트림
package lambda.lambda5.mystream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public class MyStreamV1Main {
public static void main(String[] args) {
// 짝수만 남기고 남은 값의 2배를 반환
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
returnValue(numbers);
methodChain(numbers);
}
// 메서드 체인 방식을 사용한 코드
private static void methodChain(List<Integer> numbers) {
MyStreamV1 stream = new MyStreamV1(numbers);
System.out.println("stream = " + stream.filter(n -> n%2 == 0)
.map(n->n*2)
.toList());
}
private static void returnValue(List<Integer> numbers) {
MyStreamV1 stream = new MyStreamV1(numbers);
MyStreamV1 filteredStream = stream.filter(n -> n%2 == 0);
System.out.println(filteredStream.toList());
MyStreamV1 mappedStream = filteredStream.map(n -> n*2);
System.out.println(mappedStream.toList());
}
}
-> 메서드 체인으로 코드가 더욱 간단해졌다.
정적 팩토리 메서드 사용
: 객체 생성을 담당하는 static 메서드로, 생성자 대신 인스턴스를 생성하고 반환하는 역할을 한다.
: 일반적인 생성자 대신 클래스의 인스턴스를 생성하고 초기화하는 로직을 캡슐화하여 제공하는 정적 메서드
> 정적 메서드 : 클래스 레벨에서 호출되며, 인스턴스 생성 없이 접근할 수 있음
> 객체 반환 : 내부에서 생성한 객체 반환
> 생성자 대체 : 생성자와 달리 메서드 이름을 명시할 수 있어 생성 과정의 목적이나 특징을 명확히 표현할 수 있음
> 유연한 구현 : 객체 생성 과정에서 캐싱, 객체 재활용, 하위 타입 객체 반환 등 다양한 로직을 적용할 수 있음
MyStreamV2.java
package lambda.lambda5.mystream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
// static factory 추가
public class MyStreamV2 {
private List<Integer> internalList;
// 생성자를 private으로 변경
private MyStreamV2(List<Integer> internalList) {
this.internalList = internalList;
}
// static factory
public static MyStreamV2 of(List<Integer> internalList) {
return new MyStreamV2(internalList);
}
public MyStreamV2 filter(Predicate<Integer> predicate) {
List<Integer> filtered = new ArrayList<>();
for(Integer i : internalList) {
if(predicate.test(i)) {
filtered.add(i);
}
}
return new MyStreamV2(filtered);
}
public MyStreamV2 map(Function<Integer, Integer> mapper) {
List<Integer> mapped = new ArrayList<>();
for(Integer element : internalList) {
mapped.add(mapper.apply(element));
}
return new MyStreamV2(mapped);
}
public List<Integer> toList() {
return internalList;
}
}
package lambda.lambda5.mystream;
import java.util.List;
public class MyStreamV2Main {
public static void main(String[] args) {
// 짝수만 남기고 남은 값의 2배를 반환
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
MyStreamV2.of(numbers)
.filter(n -> n%2 == 0)
.map(n -> n*2)
.toList();
}
}
"내부 반복" vs "외부 반복"
내부 반복 : 스트림 내부에서 반복 처리를 위임하는 것을 내부 반복이라고 한다. = 선언형 프로그래밍 스타일
public void forEach(Consumer<T> consumer) {
for (T element : internalList) {
consumer.accept(element);
}
}
외부 반복 : 개발자가 직접 반복 구조를 제어
List<String> result = ...
for (String s : result) {
System.out.println("name: " + s);
}
-> 내부 반복 방식은 반복 제어를 스트림에게 위임하기에 코드가 간결해진다.
-> 개발자는 "어떤 작업"을 할지 집중적으로 작성하고, "어떻게 순회할지"는 스트림이 담당하여 생산성과 가독성이 높아진다.
-> 하지만 반복 제어에 대한 복잡하고 세밀한 조정이 필요한 경우엔 외부 반복을 사용하는 것이 낫다.
-> 또한, 단순 한두 줄 수행만 하는 경우(위의 예시처럼 단순 sout)도 외부 반복(단순 for문)이 더 이해하기 쉽다.
'인프런 > 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍' 카테고리의 다른 글
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 7. 메서드 참조 (0) | 2025.05.28 |
|---|---|
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 6. 람다 vs 익명 클래스 (0) | 2025.05.28 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 4. 함수형 인터페이스 (0) | 2025.05.21 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 3. 람다 (0) | 2025.05.12 |
| [인프런] 김영한의 실전 자바 - 고급 3편, 람다, 스트림, 함수형 프로그래밍 / 2. 람다가 필요한 이유 (2) | 2025.05.12 |