스트림(Stream) / Optional<T> / 람다

2025. 7. 3. 20:48·공부일기../Java

1. 스트림이란

스트림(Stream)은 Java 8에 도입된 기능으로, 컬렉션(List, Set 등)의 요소를 함수형 스타일로 처리할 수 있게 해주는 기능

즉, 데이터를 흐름처럼 처리하면서 필터링, 변환, 정렬, 집계 등을 간결하고 선언적으로 작성가능

 

List<String> list = List.of("apple", "banana", "cherry");

// 1. 스트림 생성
// 2. 중간 연산 (filter, map 등)
// 3. 종단 연산 (collect, forEach 등)
List<String> result = list.stream()
    .filter(s -> s.startsWith("b"))       // 중간 연산: 필터링
    .map(String::toUpperCase)             // 중간 연산: 대문자 변환
    .sorted()                             // 중간 연산: 정렬
    .toList();                            // 종단 연산: 리스트 수집

 

 

📍  자주 사용하는 중간 연산

메서드  설명 예시
`filter(Predicate)` 조건에 맞는 요소만 걸러냄 list.stream().filter(s -> s.length() > 3)→ 길이 3초과인 문자열만 통과
`map(Function)` 요소를 변환 list.stream().map(String::toUpperCase)→ 모두 대문자로 변환
`flatMap(Function)` 중첩된 구조를 펼침 list.stream().flatMap(s -> Arrays.stream(s.split("")))→ 문자열을 문자 하나씩 스트림으로
`distinct()` 중복 제거 list.stream().distinct()
`sorted()` 기본 정렬 (Comparable 기준) list.stream().sorted()
`sorted(Comparator)` 사용자 정의 정렬 list.stream().sorted(Comparator.reverseOrder())
`limit(n)` 처음 n개만 선택 list.stream().limit(2)
`skip(n)` 처음 n개 건너뜀 list.stream().skip(2)
`peek(Consumer)` 디버깅용: 중간에 값 출력 등 list.stream().peek(System.out::println)

 

📍  자주 사용하는 종단 연산

메서드 설명 예시
`collect(Collectors.toList())` 결과 수집 list.stream().collect(Collectors.toList())
`forEach(Consumer)` 각 요소에 작업 수행 list.stream().forEach(System.out::println)
`count()` 요소 수 반환 list.stream().count()
`anyMatch(Predicate)` 하나라도 조건 만족 여부 list.stream().anyMatch(s -> s.startsWith("a"))
`allMatch(Predicate)` 모두 조건 만족 여부 list.stream().allMatch(s -> s.length() > 1)
`noneMatch(Predicate)` 모두 조건 불만족 여부 list.stream().noneMatch(String::isEmpty)
`findFirst()` 첫 번째 요소 반환 list.stream().findFirst() → Optional 반환
`findAny()` 아무 요소 하나 반환 list.stream().findAny()
`reduce()` 누적 계산 (합, 곱 등) list.stream().reduce("", (a, b) -> a + b)
`max(Comparator)` 최대값 찾기 list.stream().max(Comparator.naturalOrder())
`min(Comparator)` 최소값 찾기 list.stream().min(Comparator.naturalOrder())

 

1-1. 스트림의 특징 요약

  • 스트림은 데이터를 변형·처리하는데 초점 (원본 데이터 변경 X)
  • 지연 평가(Lazy Evaluation): 중간 연산은 최종 연산 전까지 수행되지 않음
  • 병렬 스트림(parallelStream())을 사용하면 멀티코어에서 병렬 처리 가능

 


 

 

2. Optional이란 

Java의 Optional<T>은 null을 안전하게 다루기 위한 컨테이너 클래스

T 타입의 값을 가질 수도 있고 없을 수도 있다.
직접 null 체크하는 대신 Optional이 제공하는 메서드들을 활용하면 널포인트 예외(NPE)를 방지하고 코드 가독성도 좋아진다.

 

2-1. Optional 주요 메서드 

메서드  설명  예제
of(value) 절대 null이 아닌 값을 감쌈 Optional.of("hello")
ofNullable(value) null일 수도 있는 값을 감쌈 Optional.ofNullable(null)
empty() 비어있는 Optional 생성 Optional.empty()

 

2-2. 값 조회 및 조건 분기

메서드  설명  예제
isPresent() 값이 존재하는지 여부 (true/false) if (opt.isPresent()) {...}
isEmpty() Java 11 이상. 값이 비었는지 확인 if (opt.isEmpty()) {...}
get() 값을 꺼냄 (비었으면 예외 발생) ❗ opt.get() (❌지양)

2-3. 기본값 처리

메서드  설명  예제
orElse(defaultValue) 값이 없으면 기본값 반환 opt.orElse("기본값")
orElseGet(Supplier) 값이 없으면 함수 실행해서 기본값 제공 opt.orElseGet(() -> "생성")
orElseThrow() 없으면 예외 발생시킴 opt.orElseThrow()
orElseThrow(Supplier) 없으면 직접 지정한 예외 발생 opt.orElseThrow(() -> new IllegalArgumentException())

2-4. 값 변형/필터링

메서드  설명  예제
map(Function) 값이 있으면 변환 후 Optional로 감쌈 opt.map(String::toUpperCase)
flatMap(Function) Optional 안에 Optional이 있을 때 펼침 opt.flatMap(...)
filter(Predicate) 조건에 맞으면 그대로, 아니면 empty 반환 opt.filter(s -> s.length() > 3)

2-5. 조건이 있을 때만 실행

메서드  설명  예제
ifPresent(Consumer) 값이 있으면 작업 수행 opt.ifPresent(System.out::println)
ifPresentOrElse(Consumer, Runnable) Java 9+값이 있으면 A, 없으면 B 수행 opt.ifPresentOrElse(..., ...)

 

Optional<String> opt = Optional.ofNullable("hello");

// 1. 값이 있으면 출력
opt.ifPresent(System.out::println); // hello

// 2. 값이 없으면 기본값
String result = opt.orElse("default"); // "hello"

// 3. 길이 5 이상인 경우만 유지
opt = opt.filter(s -> s.length() >= 5); // 그대로 유지됨

// 4. 값이 있으면 대문자로 변환
opt = opt.map(String::toUpperCase); // Optional["HELLO"]

// 5. 값이 없으면 예외 발생
String val = opt.orElseThrow(() -> new RuntimeException("값이 없어요"));

 

📍 주의사항~

  • `Optional<T>을 필드, 파라미터, 리턴 외에 쓴다면 불필요하게 복잡해짐
  • `Optional.get()`을 남용하면 값이 없으면 예외 발생한다 . (null이랑 다를 바가 없음 )
  • `Optional<T>`를 컬렉션 타입과 함께 사용 할때 불필요하다 

예를들어 `List<Optional<T>>` 또는 `Optional<List<T>>`는 불필요함 

누가 이렇게 쓸까싶지만..하다보면 또 이렇게 될수도 ..ㅋ 

 

  • Optional은 값이 "있을 수도, 없을 수도" 있는 단일 객체에 적합
  • 컬렉션(List, Set 등)은 자체적으로 비어있음을 표현할 수 있기 때문에 Optional로 감쌀 필요가 없다
  • 그래서 Optional<List<T>>, List<Optional<T>>는 대부분의 경우 과도한 추상화이며 코드를 복잡하게 만들기만 함.

 

 

- List<Optional<T>> 불필요한 이유

더보기
List<Optional<String>> list = List.of(
    Optional.of("a"),
    Optional.empty(),
    Optional.of("b")
);
  • 리스트 내부의 각 요소가 Optional인데, Optional을 꺼낼 때마다 isPresent(), get() 등을 확인해야 함.
  • 반복문 돌릴 때 매번 Optional 체크:
for (Optional<String> opt : list) {
    opt.ifPresent(System.out::println);
}
  • 실제로는 null 또는 값이 존재하는 일반 리스트 요소가 더 간단하게 처리됨:
List<String> list = List.of("a", null, "b");

 

Optional 대신 필터링이 가능한 일반 리스트를 쓰고, null 값 제거:

List<String> list = List.of("a", null, "b");

list.stream()
    .filter(Objects::nonNull)
    .forEach(System.out::println);  // a, b

 

- Optional<List<T>>가 불필요한 이유

더보기
Optional<List<String>> maybeList = findSomeList();  // List가 있을 수도 없을 수도

if (maybeList.isPresent()) {
    maybeList.get().forEach(System.out::println);
}

 

  • Optional의 존재 여부를 먼저 확인하고, 그 안의 리스트를 다시 순회해야 함. 코드 중첩.
  • 대부분의 경우, 빈 리스트 (List.of())를 반환하면 Optional이 필요 없음.

 

애초에 Optional 대신 빈 리스트 반환:

public List<String> findSomeList() {
    if (조건) return List.of("a", "b");
    else return Collections.emptyList(); // Optional 쓰지 않음
}

// 사용처
findSomeList().forEach(System.out::println); // 안전하게 처리됨

 

 


 

3. 람다란? 

  • 익명메서드~ 
    이름 없는 메서드를 간단한 식으로 표현해서 코드의 간결함과 가독성
  • Stream API나 콜백 함수, 정렬, 이벤트 처리에 자주 사용.

3-1. 기본 문법 

(매개변수) -> { 실행문 }

 

3-2. 함수형 인터페이스와 람다

 

람다는 함수형 인터페이스(Functional Interface) 가 있을 때만 사용할 수 있다.

함수형 인터페이스: 추상 메서드가 하나만 존재하는 인터페이스

예시: `Runnable`, `Comparator`, `Consumer`, `Function`, `Predicate`

Runnable r = () -> System.out.println("Thread 실행");
new Thread(r).start();

 

인터페이스  람다 형식  의미 (설명)
Consumer<T> T → void 입력만 받고 반환 없음→ System.out.println(x)
Function<T, R> T → R 입력 T를 받아서 결과 R 반환→ x -> x.length()
Predicate<T> T → boolean 입력 T를 받아 true/false 반환→ x -> x.startsWith("a")
Supplier<T> () → T 아무것도 안 받고 T를 반환→ () -> "hello"

 

3-3. 메서드 참조의 종류 4가지 

유형  예시  설명
1. Static 메서드 참조 `ClassName::staticMethod `x -> ClassName.staticMethod(x)`
2. 인스턴스 메서드 참조 (특정 객체) `instance::method` `x -> instance.method(x)`
3. 인스턴스 메서드 참조 (타입 기준) `ClassName::method` `x -> x.method()`
4. 생성자 참조 `ClassName::new` `() -> new ClassName()` 또는 `args -> new ClassName(args)`

 

3-4. 비교: 람다식 vs 메서드 참조

//1.System.out::println

list.forEach(x -> System.out.println(x));
// ↓ 동일한 동작
list.forEach(System.out::println);


//2.String::toUpperCase

list.stream()
    .map(s -> s.toUpperCase())
// ↓
    .map(String::toUpperCase);

//3. 생성자 참조
Supplier<List<String>> listSupplier = () -> new ArrayList<>();
// ↓
Supplier<List<String>> listSupplier = ArrayList::new;



 

'공부일기.. > Java' 카테고리의 다른 글

[TDD](TDD기반 서비스 개발 후) 회고 및 객체지향 설계 고민  (0) 2025.07.12
[TDD] JUnit 테스트 입문 – Mockito로 실제 객체 vs Mock 비교  (0) 2025.07.11
[TDD] TDD 테스트 방법론  (0) 2025.07.11
객제지향 속성 (캡슐화,상속,추상화,다형성)  (0) 2025.06.20
JVM 메모리영역  (6) 2025.06.19
'공부일기../Java' 카테고리의 다른 글
  • [TDD] JUnit 테스트 입문 – Mockito로 실제 객체 vs Mock 비교
  • [TDD] TDD 테스트 방법론
  • 객제지향 속성 (캡슐화,상속,추상화,다형성)
  • JVM 메모리영역
s0-0mzzang
s0-0mzzang
공부한것을 기록합니다...
  • s0-0mzzang
    승민이의..개발일기..🐰
    s0-0mzzang
  • 전체
    오늘
    어제
    • 전체~ (108)
      • 마음가짐..! (10)
      • 공부일기.. (76)
        • weekly-log (6)
        • Spring (19)
        • Java (18)
        • DataBase (10)
        • git (2)
        • JPA (6)
        • kafka (1)
        • Backend Architecture (3)
        • Troubleshooting (삽질..ㅋ) (2)
        • Cloud (1)
        • Docker (2)
        • 알고리즘 (1)
        • 리액트 (2)
        • Infra (3)
      • 하루일기.. (22)
        • 그림일기 (8)
        • 생각일기 (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    항해플러스
    ADC 환경
    spring boot
    ERD
    React
    리팩토링
    spring
    JPA
    swagger
    다짐
    자바
    BufferedReader
    스프링부트
    항해99
    MySQL
    TDD
    StringTokenizer
    Paging
    SpringBoot
    인프라 기초
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
s0-0mzzang
스트림(Stream) / Optional<T> / 람다
상단으로

티스토리툴바