중간 연산
생성된 스트림을 필터링하거나 원하는 형태로 가공하는 과정
중간 연산은 스트림을 반환하기 때문에 여러 작업을 이어서 호출하는 Chaining이 가능하다.
스트림 필터링
filter
스트림 내 요소들을 특정 조건(predicate)에 맞게 걸러낸 요소만으로 구성된 새로운 스트림 반환
조건에 해당하는 Predicate<T> 를 인자로 받는다.
매개변수 하나를 입력받아 boolean 타입으로 반환하는 함수형 인터페이스
조건식을 표현하는데 사용된다.
List<String> language = Arrays.asList("java", "kotlin", "python", "c", "go");
language.stream().filter(s -> s.contains("o")).forEach(lang -> System.out.printf("%s ", lang));
// kotlin python go
distinct
스트림 내 요소의 중복 제거한 후 새로운 스트림을 반환
Stream<String> duplicateStream = Arrays.stream(new String[]{"a", "b", "c", "a", "d", "b"});
duplicateStream.distinct().forEach(s -> System.out.printf("%s ", s));
기본 타입이 아닌 custom class (VO class, object)의 경우 equals(), hashCode()를 재정의 해야한다.
// distinct
List<Person> persons = Arrays.asList(new Person("Joy"), new Person("Joe"),
new Person("Zoy"), new Person("Joy"));
Stream<Person> personsStream = persons.stream();
personsStream.distinct().forEach(p -> System.out.printf("%s ", p.name));
// Joy Joe Zoy
// custom class
private static class Person {
private final String name;
public Person(String name) {
this.name = name;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Person)
return name.equals(((Person) obj).name);
return false;
}
}
스트림 변환
map
단일 스트림의 요소들을 주어진 함수에 인수로 전달하여 매핑시킨 후, 매핑된 값을 다시 스트림으로 반환
Function<T, R> 을 인자로 받는다.
T 타입의 인자를 받아 R 타입의 객체를 반환하는 함수형 인터페이스
형변환 시 사용한다.
List<String> language = Arrays.asList("java", "kotlin", "python", "c", "go");
language.stream().map(String::toUpperCase).forEach(lang -> System.out.printf("%s ", lang));
// JAVA KOTLIN PYTHON C GO
flatMap
Array나 Object로 감싸져 있는 모든 원소를 단일 원소 스트림으로 반환
flattening + map()
Flattening
여러 컬렉션 / 배열을 하나로 병합하는 것
Before flattening : [[1,2,3], [4,5], [6,7,8]]
After flattening : [1,2,3,4,5,6,7,8]
사용 예제
// example 1
String[][] dataArray = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}, {"g", "h"}};
List<String> listOfAllChars = Arrays.stream(dataArray)
.flatMap(Arrays::stream)
.collect(Collectors.toList());
System.out.println(listOfAllChars);
// [a, b, c, d, e, f, g, h]
// example2
String[] arr = {"Hello Af3","This is", "flatmap example"};
Stream<String> stream = Arrays.stream(arr);
stream.flatMap(s -> Stream.of(s.split(" +"))).forEach(System.out::println);
// 결과
Hello
Af3
This
is
flatmap
example
활용
- 중간 연산 메소드는 컬렉션 스트림에서는 동작하지 않기 때문에 중간 메소드 사용 이전에 스트림을 flattening시 사용
flatMap vs map
map : 1:1 반환
flatMap: 1: N 반환
스트림 정렬
sorted
스트램 내 요소를 정렬하여 새 스트림을 반환
인자없이 호출할 경우 오름차순으로 정렬하고, 정렬 조건이 필요할 경우 Comparator<T>를 인자로 줄 수도 있다.
Comparable이 구현된 클래스들의 기본 정렬 기준과 다르게 정렬하고 싶을 때 사용하는 인터페이스
Compare()를 override 하여 정렬 조건을 구현한다.
IntStream.of(4, 2, 5, 6, 7, 1, 3).sorted().forEach(num -> System.out.printf("%d ", num));
// 1 2 3 4 5 6 7
// comparator
List<String> language = Arrays.asList("java", "kotlin", "python", "c", "go");
language.stream().sorted(Comparator.comparing(String::length))
.forEach(lang -> System.out.printf("%s ", lang));
//c go java kotlin python
스트림 제한
skip
스트림 내 첫 번째 요소 ~ 전달된 개수 만큼의 요소를 제외한 나머지 요소로 구성된 새로운 스트림을 반환
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.filter(i -> i % 2 == 0)
.skip(2)
.forEach(i -> System.out.print(i + " "));
// 6 8 10
limit
스트림에서 첫 번째 요소부터 전달된 개수만큼의 요소만으로 이루어진 새로운 스트림을 반환
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.filter(i -> i % 2 == 0)
.limit(2)
.forEach(i -> System.out.print(i + " "));
// 2 4
skip과 limit 의 차이점 (https://www.baeldung.com/java-stream-skip-vs-limit)
무한 스트림을 자를 때는 limit(), 유한 스트림을 자를 때는 skip()을 사용하는게 유용하다.
스트림 조회
peek
스트림은 재사용이 불가능하기 때문에 종단연산이 호출된 이후 해당 스트림을 호출할 경우,
java.lang.IllegalStateException: stream has already been operated upon or closed 이런 오류가 발생한다.
그러나 Stream 연산 중간에 결과를 확인하고 싶을 수도 있다. 그럴 때 peek 을 호출하면 된다.
peek은 종단 연산이 아니라 stream을 반환하는 중간 연산이기 때문에 연산 사이에 여러 번 호출해도 문제가 되지 않는다.
대신 종단 연산을 호출하지 않으면 스트림 자체가 동작하지 않기 때문에 주의해야 한다.
List<Integer> numbers = Arrays.asList(2, 3, 4, 5);
numbers.stream()
.peek(x -> System.out.println("\nStart Debug"))
.peek(x -> System.out.println("from stream : " + x))
.map(x -> x + 17)
.peek(x -> System.out.println("after stream : " + x))
.filter(x -> x % 2 == 0)
.peek(x -> System.out.println("after filter : " + x))
.limit(3)
.peek(x -> System.out.println("after limit : " + x))
.forEach(System.out::println);
// 결과
Start Debug
from stream : 2
after stream : 19
Start Debug
from stream : 3
after stream : 20
after filter : 20
after limit : 20
20
Start Debug
from stream : 4
after stream : 21
Start Debug
from stream : 5
after stream : 22
after filter : 22
after limit : 22
22
// [출처] https://goodgid.github.io/Java-8-Stream-Debug-Peek/
[참고]
http://tcpschool.com/java/java_stream_intermediate
'Java' 카테고리의 다른 글
[스트림 활용] 생성 (0) | 2021.05.18 |
---|---|
[Effective Java 7장] 람다 (0) | 2021.05.09 |