728x90
요약:min·max는 비교 기준(Comparator)에 따라 가장 작은/큰 요소를 찾는 최종 연산입니다. 숫자의 극값은 물론, "가장 긴 문자열", "가장 비싼 상품"처럼 객체의 극값도 찾을 수 있죠. 이 글에서 사용법,Comparator지정, 결과가Optional로 감싸지는 이유, 그리고 숫자 스트림의 축약형을 정리합니다. 파트1 기초편의 마지막입니다.
"최고 점수는?", "가장 싼 상품은?", "제일 최근 주문은?" — 극값(min/max) 찾기도 집계만큼이나 자주 나옵니다. for 루프로 "현재 최댓값 변수"를 두고 하나씩 비교하던 그 로직을, 스트림은 max(Comparator) 한 줄로 끝냅니다.
중요한 건 min·max도 Optional을 반환한다는 점입니다. 스트림이 비어 있으면 "가장 큰 값"이 존재하지 않으니까요. 6편의 average()가 OptionalDouble이었던 것과 같은 맥락입니다. 면접에서 "min/max의 반환 타입은?"이 함정으로 나옵니다.

문제 정의
문제. ① 정수 리스트의 최솟값·최댓값을 구하고, ② 문자열 리스트에서 가장 긴 문자열을 찾아라.
- 입력:
List<Integer> nums = [42, 17, 88, 5, 63]List<String> words = ["java", "stream", "api", "lambda"]- 기대 출력:
- 최솟값:
5 - 최댓값:
88 - 가장 긴 문자열:
stream
전통 방식(for-loop) — 그리고 스트림이 나은 이유
import java.util.List;
public class Traditional {
public static void main(String[] args) {
List<Integer> nums = List.of(42, 17, 88, 5, 63);
int min = nums.get(0);
int max = nums.get(0);
for (int n : nums) {
if (n < min) min = n; // 최솟값 갱신
if (n > max) max = n; // 최댓값 갱신
}
System.out.println(min);
System.out.println(max);
}
}
"초깃값을 첫 요소로 두고, 돌면서 갱신"하는 패턴입니다. 빈 리스트면 get(0)에서 예외가 나고, 객체 기준 비교로 바뀌면 코드가 더 길어집니다. 스트림 min·max는 비교 기준만 넘기면 되고, 빈 경우도 Optional로 안전하게 다룹니다.
스트림 해법 — min · max

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class MinMaxExample {
public static void main(String[] args) {
List<Integer> nums = List.of(42, 17, 88, 5, 63);
// ① 최솟값·최댓값 (자연 순서 기준)
Optional<Integer> min = nums.stream().min(Comparator.naturalOrder());
Optional<Integer> max = nums.stream().max(Comparator.naturalOrder());
System.out.println(min.get());
System.out.println(max.get());
// ② 객체 극값: 가장 긴 문자열
List<String> words = List.of("java", "stream", "api", "lambda");
Optional<String> longest = words.stream()
.max(Comparator.comparingInt(String::length));
System.out.println(longest.orElse("(없음)"));
// (보너스) 숫자 스트림은 IntStream의 min/max가 더 간결
int intMin = nums.stream().mapToInt(Integer::intValue).min().getAsInt();
System.out.println(intMin);
}
}
한 줄씩 설명
.min(Comparator.naturalOrder())— 자연 순서 기준으로 가장 작은 요소를 찾습니다.Comparator.naturalOrder()는 "작은 값이 먼저"라는 기본 비교자입니다..max(Comparator.naturalOrder())— 같은 기준으로 가장 큰 요소를 찾습니다..max(Comparator.comparingInt(String::length))— "길이가 가장 큰" 문자열을 찾습니다. 기준을Comparator로 바꾸면 어떤 필드로든 극값을 구할 수 있습니다. (java·api도 있지만stream·lambda가 6글자로 가장 길고, 먼저 나온stream이 선택됩니다.)mapToInt(...).min().getAsInt()— 숫자라면IntStream의min()이 더 간결합니다. 이때 반환은OptionalInt이고getAsInt()로 꺼냅니다.
실행 결과
5
88
stream
5
변형·함정·주의
- ⚠ 반환은
Optional.min·max는 빈 스트림일 수 있어Optional<T>(기본형은OptionalInt등)을 반환합니다.get()전에isPresent()를 확인하거나orElse(기본값)로 안전하게 꺼내세요. 빈 스트림에get()하면NoSuchElementException입니다. - 동점 처리. 비교 기준으로 같은 값이 여러 개면 보통 먼저 만난 요소가 선택됩니다(구현 의존이 아닌, 안정적 동작). 특정 요소를 원하면
thenComparing으로 2차 기준을 더하세요. min은max의 역.max(comp)는min(comp.reversed())와 같습니다. 기준 하나만 뒤집으면 방향이 바뀝니다.- 집계와의 관계. 숫자 극값만 필요하면 6편의
summaryStatistics()가getMin()·getMax()를 한 번의 순회로 함께 줍니다. min·max·sum·average를 모두 쓸 땐 이쪽이 효율적입니다.
관련 면접 질문
- Q1.
min·max의 반환 타입이Optional인 이유는?
→ 스트림이 비어 있으면 극값이 존재하지 않기 때문입니다. Optional로 감싸 "값이 없을 수 있음"을 타입으로 표현하고, NoSuchElementException을 예방합니다.
- Q2. 객체 리스트에서 특정 필드 기준 최댓값을 찾으려면?
→ stream.max(Comparator.comparing(Obj::getField))를 씁니다. 숫자 필드는 comparingInt/comparingDouble이 박싱이 없어 더 낫고, 동점 시 2차 기준은 thenComparing으로 지정합니다.
시리즈 네비게이션
- ⬆️ 📎 목차 허브 — 자바 스트림 코딩 문제 41선 총정리 (0편으로 돌아가기)
- ⬅️ 📎 이전 편 — [자바 스트림 9] limit·skip 페이징
- ➡️ 📎 다음 편 — [자바 스트림 11] flatMap으로 중첩 평탄화 (파트2 중급 시작, 발행 시 연결)
- 📎 함께 읽기 — Java 25 LTS 새 기능 총정리 (최신 자바 문법 레퍼런스)
🎉 이것으로 파트1 기초 10편이 끝났습니다. 스트림 생성·변환·필터·수집·집계·정렬·중복·페이징·극값까지, 실무 코드의 90%를 읽을 기본기를 갖추셨습니다. 다음 편부터는 파트2 중급 —flatMap·reduce·groupingBy같은 코테·면접의 진짜 핵심이 이어집니다.
'쿤즈 Dev > Java' 카테고리의 다른 글
| [자바 스트림 12] reduce로 누적 계산 — 집계 로직을 직접 설계한다 (0) | 2026.10.28 |
|---|---|
| [자바 스트림 11] flatMap으로 중첩 평탄화 — 리스트 속 리스트를 한 줄로 (0) | 2026.10.27 |
| 자바 스트림, 이렇게 쓰면 느려진다 — 성능 함정 8가지와 개선법 (Java 21) (0) | 2026.10.25 |
| [자바 스트림 9] limit·skip 페이징 — 앞에서 자르고 건너뛰기 (0) | 2026.10.25 |
| [자바 스트림 8] distinct로 중복 제거 — 유니크한 값만 남기기 (0) | 2026.10.24 |
댓글