랩터
[Java] 스트림 - 스트림의 중간 연산 본문
전체적인 코드 구조를 파악해 보도록 하겠습니다.
중간연산에서 가장 많이 사용되는 필터링,매핑,정렬 등에 대해 살펴보겠습니다.
필터링(filter() , distinct() )
필터링은 이름 그대로 우리의 필요에 따라 조건에 맞는 데이터들만을 정제하는 역할을 하는 중간 연산자를 가리킵니다.
- distinct() : Stream의 요소들에 중복된 데이터가 존재하는 경우, 중복을 제거하기 위해 사용합니다.
- filter(): Stream에서 조건에 맞는 데이터만을 정제하여 더 작은 컬렉션을 만들어냅니다. filter()메서드에는 매개값으로 조건을 주고, 조건이 참이 되는 요소만 필터링합니다. 여기서 조건은 람다식을 사용하여 정의할 수 있습니다.
package Stream;
import java.util.Arrays;
import java.util.List;
public class FilteringExample {
public static void main(String[] args) throws Exception{
List<String> names = Arrays.asList("김코딩", "이자바", "박해커", "김코딩", "박해커");
names.stream()
.distinct() // 중복 제거
.forEach(element -> System.out.println(element));
System.out.println();
names.stream()
.filter(element -> element.startsWith("김"))//김씨 성을 가진 요소만 필터링
.forEach(element -> System.out.println(element));
System.out.println();
names.stream()
.distinct() // 중복제거
.filter(element -> element.startsWith("김"))//김씨 성을 가진 요소만 필터링
.forEach(element -> System.out.println(element));
}
}
//출력값
김코딩
이자바
박해커
김코딩
김코딩
김코딩
첫 번째와 두 번째 스트림은 각각 독립적인 스트림입니다. 대표적인 최종 연산자인 forEach()로 첫 번째 스트림이 닫히고 난 후에 두 번째 스트림이 생성되어 새로운 연산을 수행하고 있습니다.
매핑(map())
매핑은 스트림 내 요소들에서 원하는 필드만 추출하거나 특정 형태로 변환할 때 사용하는 중간 연산자입니다.
위의 필터 메서드와 마찬가지로 값을 변환하기 위한 조건을 람다식으로 정의합니다.
package Stream;
import java.util.Arrays;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("kimcoding", "javalee", "hackerna", "luckyguy");
names.stream()
.map(element -> element.toUpperCase()) // 요소들을 하나씩 대문자로 변환
.forEach(element -> System.out.println(element));
}
}
//출력값
KIMCODING
JAVALEE
HACKERNA
LUCKYGUY
names안에 정의된 각 요소를 순회하면서 소문자 이름을 대문자로 변환한 값들이 담긴 스트림으로 반환하는 연산 과정코드입니다.
package Stream;
import java.util.Arrays;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,3,6,9);
//각 요소에 3을 곱한 값을 반환
list.stream().map(number -> number * 3).forEach(System.out::println);
}
}
map()과 flatMap()의 차이
package Stream;
import java.util.Arrays;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args) {
String[][] namesArray = new String[][]{{"박해커", "이자바"},{"김코딩","나박사"}};
Arrays.stream(namesArray)
.map(inner -> Arrays.stream(inner))
.forEach(System.out::println);
}
}
//출력값
java.util.stream.ReferencePipeline$Head@5b6f7412
java.util.stream.ReferencePipeline$Head@27973e9b
위의 메서드는 Stream<Stream<String>> 중첩 스트림을 반환하고 있기 때문에 원하는 결과값을 출력할 수 없습니다.
Stream<String>이 되어야 원하는 결과값을 얻을 수 있습니다.
// map 사용
Arrays.stream(namesArray)
.map(inner -> Arrays.stream(inner))
.forEach(names -> names.forEach(System.out::println));
// 출력값
박해커
이자바
김코딩
나박사
forEach() 메서드 안의 람다식의 정의에서, 각 요소에 대하여 다시 forEach() 메서드를 출력함으로써 뎁스가 있는 요소들에 접근하여 이를 출력할 수 있습니다.
하지만 지금처럼 이중구조가 아닌 뎁스가 3중, 4중, 5중으로 깊어지는 경우는 어떨까요?
아마 코드를 작성하는 개발자 입장에서도 매우 번거롭고 작성된 코드 또한 가독성이 떨어질 것입니다.
이런 경우, 우리는 flatMap()을 활용할 수 있습니다.
// flatMap()
Arrays.stream(namesArray).flatMap(Arrays::stream).forEach(System.out::println);
// 출력값
박해커
이자바
김코딩
나박사
정렬(sorted())
sorted() 메서드는 이름처럼 정렬할 때 사용하는 중간 연산자입니다.
package Stream;
import java.util.Arrays;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args) {
List<String> animals = Arrays.asList("Tiger", "Lion", "Monkey", "Duck", "Horse", "Cow");
//인자값 없는 sort() 호출
animals.stream().sorted().forEach(System.out::println);
}
}
괄호 안에 Comparator라는 인터페이스에 정의된 static 메서드와 디폴트 메서드를 사용하여 간편하게 정렬 작업을 수행할 수 있습니다. 괄호 안에 아무 값도 넣지 않은 상태로 호출하면 기본 정렬(오름차순)로 정렬됩니다.
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class IntermediateOperationExample {
public static void main(String[] args) {
List<String> animals = Arrays.asList("Tiger", "Lion", "Monkey", "Duck", "Horse", "Cow");
// 인자값에 Comparator 인터페이스에 규정된 메서드 사용
animals.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
}
}
// 출력값
Tiger
Monkey
Lion
Horse
Duck
Cow
기타
skip() - 스트림의 일부요소들을 건너뜁니다.
import java.util.stream.IntStream;
public class IntermediateOperationExample {
public static void main(String[] args) {
// 1~10 범위의 정수로 구성된 스트림 생성
IntStream intStream = IntStream.rangeClosed(1, 10);
// 앞의 5개의 숫자를 건너뛰고 숫자 6부터 출력
intStream.skip(5).forEach(System.out::println);
}
}
// 출력값
6
7
8
9
10
limit() - 스트림의 일부를 자릅니다
import java.util.stream.IntStream;
public class IntermediateOperationExample {
public static void main(String[] args) {
// 1~10 범위의 정수로 구성된 스트림 생성
IntStream intStream = IntStream.rangeClosed(1, 10);
// 앞에서부터 5개의 숫자만 출력
intStream.limit(5).forEach(System.out::println);
}
}
// 출력값
1
2
3
4
5
peek() - forEach()와 마찬가지로, 요소들을 순회하며 특정 작업을 수행합니다.
peek()는 중간연산자여서 여러번 사용 가능하지만, forEach()는 최종연산자여서 단 한번만 사용가능합니다.
peek의 특성때문에 코드의 에러를 찾기위한 디버깅 용도로 종종 활용됩니다.
import java.util.stream.IntStream;
public class IntermediateOperationExample {
public static void main(String[] args) {
// 요소들을 사용하여 IntStream 생성
IntStream intStream3 = IntStream.of(1, 2, 2, 3, 3, 4, 5, 5, 7, 7, 7, 8);
// 짝수만 필터링하여 합계 구하기
int sum = intStream3.filter(element -> element % 2 == 0)
.peek(System.out::println)
.sum();
System.out.println("합계 = " + sum);
}
}
// 출력값
2
2
4
8
합계 = 16
'공부 > JAVA' 카테고리의 다른 글
[Java] 스트림 - Optional Class (0) | 2024.07.09 |
---|---|
[Java] 스트림 - 스트림의 최종 연산 (0) | 2024.07.08 |
[Java] 스트림 - 스트림의 생성 (0) | 2024.07.08 |
[Java] 스트림 - 핵심 개념과 특징 (0) | 2024.07.08 |
[Java] 람다 (1) | 2024.07.05 |