알고리즘 문제(문자열 내 마음대로 정렬하기)를 풀다가 java stream 사용법에 대해 궁금해졌어요.
해당 문제에선 각 단어마다 특정 index에 있는 값들을 기준으로 정렬하고, 같으면 사전순으로 정렬하는 문제예요.
stream을 사용하면 정렬하기 쉽겠다..! 생각은 했지만, 막상 쓰려고 하니 문법을 정확히 몰라 이렇게 정리해봤습니다!
Stream이란?
Stream은 데이터를 함수형 스타일로 처리할 수 있게 해주는 API
반복문을 쓰지 않고 필터링, 정렬, 매핑, 집계 같은 작업을 “파이프라인” 방식으로 처리할 수 있다.
Stream 기초 문법
1. Stream 생성하기
- 리스트에서 스트림 생성
List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> stream1 = list.stream();
- 배열에서 스트림 생성
String[] arr = {"dog", "Cat", "bird"};
Stream<String> stream2 = Arrays.stream(arr);
- 직접 값을 넣어서 생성
Stream<Integer> stream3 = Stream.of(1,2,3,4,5);
2. 중간 계산
filter() : 조건에 맞는 데이터만 걸러내기
List<Integer> nums = Arrays.asList(1,2,3,4); List<Integer> even = nums.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); // [2,4]map() : 데이터 반환하기
List<String> words = Arrays.asList("java", "python", "c"); List<Integer> lengths = words.stream() .map(String::length) .collect(Collectors.toList()); // [4, 6, 1]sorted() : 정렬
List<String> words = Arrays.asList("banana", "apple", "cherry"); List<String> sorted = words.stream() .sorted() .collect(Collectors.toList()); // [apple, banana, cherry]distinct() : 중복제거
List<Integer> nums = Arrays.asList(1, 2, 2, 3, 3, 3); List<Integer> distinct = nums.stream() .distinct() .collect(Collectors.toList()); // [1, 2, 3]limit() : 앞에서 N개만 자르기
List<Integer> nums = Arrays.asList(10, 20, 30, 40, 50); List<Integer> limited = nums.stream() .limit(3) .collect(Collectors.toList()); // [10, 20, 30]skip() : 앞에서 N개 건너뛰기
List<Integer> nums = Arrays.asList(10, 20, 30, 40, 50); List<Integer> skipped = nums.stream() .skip(2) .collect(Collectors.toList()); // [30, 40, 50]sum() : 합계
int[] nums = {1,2,3,4,5}; int sum = Arrays.stream(nums).sum();
3. 최종 계산
collect() : 결과를 컬렉션이나 배열로 반환
List<String> words = Arrays.asList("a", "b", "c"); Set<String> set = words.stream() .collect(Collectors.toSet()); // 컬렉션으로 변환forEach() : 각 요소에 대한 동작 수행
List<String> words = Arrays.asList("a", "b", "c"); words.stream() .forEach(w -> System.out.print(w + " "));count() : 개수 세기
long cnt = Stream.of("java", "c", "python").count();reduce() : 누적 연산(합계, 곱 등)
int sum = Stream.of(1,2,3,4,5) .reduce(0, (a, b) -> a + b); // 24findFirst(), findAny() : 요소 하나 반환
Optional<String> first = Stream.of("java", "python", "c").findFirst();Optional<String> any = Stream.of("java", "python", "c").findAny();allMatch(), anyMatch(), noneMatch() : 조건 검사boolean allEven = Stream.of(2, 4, 6).allMatch(n -> n % 2 == 0); // 모든 요소가 조건을 만족하는지 검사 // trueboolean hasNegative = Stream.of(1, -2, 3).anyMatch(n -> n < 0); // 하나라도 조건을 만족하는지 검사 // trueboolean noneNegative = Stream.of(1, 2, 3).noneMatch(n -> n < 0); // 모든 요소가 조건을 만족하지 않는지 검사 // true
코테에서 자주 쓰이는 Stream 쓰임 (정렬편)
Stream으로 정렬하기
Stream.sorted()는 내부적으로 Comparator를 받아서 동작한다.
함수형으로도 표현할 수 있고, Comparator로도 표현할 수 있다.
Comparator 람다식 규칙도 헷갈려서 정리하고 간다.
Comparator 람다식 규칙
Comparator는 (a,b) 두 값을 비교해서 정수(int)를 반환해야한다.
- 음수 반환 → a가 b보다 “작다” → a가 앞으로 감 (오름차순)
- 0 반환 → 두 값이 같다 → 순서 유지
- 양수 반환 → a가 b보다 “크다” → a가 뒤로 감
즉, 정렬 방향은 “반환값의 부호”에 따라 결정된다.
예를 들어 a=2, b=5일 때:
a - b = 2 - 5 = -3 → 음수
→ a가 b보다 작으니 a를 앞으로 보냄
반대로 a=9, b=4일 때:
a - b = 9 - 4 = 5 → 양수
→ a가 크니 b가 앞으로, a는 뒤로 감
➡️ 결국 a - b는 자연스러운 오름차순 비교가 된다.
이 과정을 생각하고! 코테에서 자주 쓰이는 Stream 정렬 예시 코드를 보자.
1️⃣ 문자열 길이 기준 정렬
Comparator
List<String> words = Arrays.asList("apple", "kiwi", "banan", "cherry");
List<String> sortedWords = words.stream()
.sorted(Comparator.comparingInt(String::length))
.toList();
// [kiwi, apple, banana, cherry]
함수형
List<String> sortedWords = words.stream()
.sorted((s1, s2) -> Integer.compare(s1.length(), s2.length())) // 길이 기준 오름차순
.toList();
여기서 잠깐,
toList() 과 collect(Collectors.toList()) ???? 둘의 차이는 뭘까.
- toList() : java 16 부터 지원
- collect(Collectors.toList()) : java 8부터 지원
코테에선 보통 java 8부터 지원하므로 앞으론 collect(Collectors.toList())을 사용하겠다.
2️⃣ 내림차순 정렬
Comparator
List<Integer> numbers = Arrays.asList(5, 2, 9, 1, 7);
List<Integer> sortedNumbers = numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
// [9, 7, 5, 2, 1]
함수형
List<Integer> numbers = Arrays.asList(5, 2, 9, 1, 7);
List<Integer> sorted = numbers.stream()
.sorted((a, b) -> b - a) // 내림차순
.toList();
System.out.println(sorted); // [9, 7, 5, 2, 1]
3️⃣ 특정 인덱스 문자 기준 정렬 (+ 같으면 사전순)
Comparator
List<String> words = Arrays.asList("sun", "bed", "car");
int n = 1;
List<String> sortedWords = words.stream()
.sorted(
Comparator.comparing((String s) -> s.charAt(n))
.thenComparing(Comparator.naturalOrder())
)
.collect(Collectors.toList());
// [car, bed, sun]
함수형
List<String> words = Arrays.asList("sun", "bed", "car");
int n = 1;
List<String> sorted = words.stream()
.sorted((s1, s2) -> {
if (s1.charAt(n) == s2.charAt(n)) {
return s1.compareTo(s2); // 같으면 사전순
}
return Character.compare(s1.charAt(n), s2.charAt(n)); // n번째 문자 비교
})
.toList();
System.out.println(sorted); // [car, bed, sun]
4️⃣ 길이 → 첫글자 → 사전순 정렬
Comparator
List<String> words = Arrays.asList("dog", "apple", "ant", "banana", "car");
List<String> sortedWords = words.stream()
.sorted(
Comparator.comparingInt(String::length) // 1. 길이 기준
.thenComparing(s -> s.charAt(0)) // 2. 첫글자
.thenComparing(Comparator.naturalOrder()) // 3. 사전순
)
.collect(Collectors.toList();
함수형
List<String> words = Arrays.asList("dog", "apple", "ant", "banana", "car");
List<String> sorted = words.stream()
.sorted((s1, s2) -> {
if(s1.length() != s2.length()){
return s1.length() - s2.length();
}
if (s1.charAt(0) != s2.charAt(0)){
return Character.compare(s1.charAt(0), s2.charAt(0));
}
return s1.compareTo(s2);
})
.collect(Collectors.toList());
이상, java 정렬 문제에 쫄지말자!
'Java' 카테고리의 다른 글
| intellij 빌드 및 실행 오류 : 올바른 springboot 애플리케이션 클래스가 아닙니다 (0) | 2025.03.21 |
|---|---|
| [아키텍처] Service & Repository & Domain & MVC (0) | 2024.06.21 |