티스토리 뷰
개념
- 스트림에는 중간 연산의 수행 결과를 디버깅할 수 있는 수단인
peek
메서드를 제공한다.
-
파라미터로 주어지는
Consumer<? super T> action
를 사용해서 스트림의 요소들을 소모한다.Consumer<T>
를 받는 만큼 스트림의 요소들을 가지고 추가적은 작업을 수행할 수도 있다.
-
peek
메서드와forEach
메서드를 혼동해서는 안된다.peek
메서드와forEach
메서드 모두System.out::println
같은Consumer<T>
를 파라미터로 받는다.forEach
메서드는 최종 연산이기 때문에 결과를 확인할 수 있으나peek
메서드는 중간 연산이기 때문에 어떠한 최종 연산도 하지 않으면 아무것도 확인할 수가 없다.
-
보통 잘 사용되지 않는 메서드지만 알아둘 필요는 있다고 생각한다.
예제
-
아래 예제에서 사용되는 내용들은 다양한 상황을 연출하기 위해서 임의로 만들어진 내용이므로 특정 속성에 대해서 왜 저 타입이 사용되었는지 의문을 가지지 말자!
-
예제 코드에서 사용되고 있는 스펙
- JUnit 5
- Gradle 6.7
기본 데이터 생성
public class Address {
private final int no;
private final String si;
private final String gu;
private final String dong;
private String fullName;
public Address(int no, String si, String gu, String dong, String fullName) {
this.no = no;
this.si = si;
this.gu = gu;
this.dong = dong;
this.fullName = fullName;
}
public String getSi() {
return si;
}
public String getGu() {
return gu;
}
public String getDong() {
return dong;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
@Override
public boolean equals(Object obj) {
final var address = (Address) obj;
return no == address.no && si.equals(address.getSi())
&& gu.equals(address.getGu()) && dong.equals(address.getDong())
&& ((fullName == null && address.getFullName() == null) || fullName.equals(address.getFullName()));
}
@Override
public String toString() {
return "Address{" +
"no=" + no +
", si='" + si + '\'' +
", gu='" + gu + '\'' +
", dong='" + dong + '\'' +
", fullName='" + fullName + '\'' +
'}';
}
}
현재 Java의 단점이 부각되는 코드인 것 같다. 데이터 클래스를 하나 만드는데 너무 많은 내용이 필요하다.
private final PeekUsage peekUsage = new PeekUsage();
private final List<Address> addresses = List.of(
new Address(1, "서울시", "관악구", "신림동", null),
new Address(2, "서울시", "강남구", "논현동", null),
new Address(3, "서울시", "동작구", "사당동", null),
new Address(4, "서울시", "서초구", "양재동", null));
두 수의 공배수 구하기
- 주어진 두 수와 최댓값 n이 주어질 때 두 수의 공배수를 구해보자.
public List<Integer> getCommonMultipleAAndBUntilN(int a, int b, int n) {
return IntStream.rangeClosed(1, n)
.filter(i -> i % a == 0)
.peek(i -> System.out.println(a + "의 배수: " + i))
.filter(i -> i % b == 0)
.peek(i -> System.out.println(a + "와 " + b + "의 공배수: " + i))
.boxed()
.collect(Collectors.toList());
}
-
IntStream.rangedClosed(1, n)
을 이용해서 1부터 n까지의 자연수를 생성하는 스트림을 만들었다. -
두 번의
filter
메서드를 통해서 배수 판별을 하고 중간중간에peek
메서드를 사용해서 디버깅을 하고 있다. -
collect
메서드를 호출하기 전에boxed
메서드를 사용해서 기본형 특화 스트림의 primitive 타입을 Wrapper 클래스 타입으로 박싱한다.- 박싱을 하지 않으면
collect(Collectors.toList())
를 사용해서List<Integer>
로 모을 수 없다. - 여기서 사용되는
boxed
라는 메서드 또한 중간 연산 중 하나지만 내용 자체가 기본형 특화 스트림을 그에 해당하는 Wrapper 클래스 타입의 스트림으로 박싱하는 역할만 하기 때문에 별도로 포스팅하지는 않을 것이다. IntStream
을Stream<Integer>
,LongStream
을Stream<Long>
그리고DoubleStream
을Stream<Double>
로 변환시킨다.
- 박싱을 하지 않으면
-
15 이하의 자연수 중에서 2와 3의 공배수를 구해보자.
@Test
@DisplayName("두 수의 공배수 구하기")
void getCommonMultipleAAndBUntilNTest() {
final var expected = List.of(6, 12);
final var result = peekUsage.getCommonMultipleAAndBUntilN(2, 3, 15);
assertEquals(expected, result);
}
- 다음과 같은 내용이 콘솔에 출력되고 테스트가 성공한다.
주소 풀네임 조합하기
- 시, 구, 동 데이터가 있을 때 이것을 합쳐서 전체 주소로 만들어보자.
public List<Address> makeFullNameInAddress(List<Address> addresses) {
return addresses.stream()
.peek(a -> a.setFullName(a.getSi() + " " + a.getGu() + " " + a.getDong()))
.collect(Collectors.toList());
}
-
여기서는
peek
메서드를 사용해서 시, 구, 동을 하나의 문자열로 합치는 작업을 수행했다.- 개인적으로 이런 방식을 선호하지는 않는다.
- 객체 내부의 값을 변경하는 방식 자체는 부수 효과를 유발할 수 있고 동기화 문제에 대해서도 별도의 처리가 필요하기 때문이다.
map
을 통해 매번 새로운 객체를 생성하는 방법으로 유사한 동작을 할 수 있다.- 하지만 실제로 코드를 짜다보면 객체를 새로 생성하는 것에 대한 비용이 크다면
peek
메서드 사용을 고려해 볼 수는 있다. - 상황에 맞는 적절한 방식을 찾는 것이 중요한 것 같다.
@Test
@DisplayName("주소 풀네임 조합하기")
void getEvenNumbersCountTest() {
final var expected = List.of(
new Address(1, "서울시", "관악구", "신림동", "서울시 관악구 신림동"),
new Address(2, "서울시", "강남구", "논현동", "서울시 강남구 논현동"),
new Address(3, "서울시", "동작구", "사당동", "서울시 동작구 사당동"),
new Address(4, "서울시", "서초구", "양재동", "서울시 서초구 양재동"));
final var result = peekUsage.makeFullNameInAddress(addresses);
result.forEach(System.out::println);
assertIterableEquals(expected, result);
}
- 다음과 같은 내용이 콘솔에 출력되고 테스트가 성공한다.
이상 Stream API에서 사용하는 중간 연산 중 peek
메서드에 대해 살펴보았다.
'프로그래밍 > Java' 카테고리의 다른 글
[Modern Java] 동작 파라미터화(Behavior Parameterization) (2) | 2021.01.17 |
---|---|
[Stream API] 중간 연산 - sorted 메서드 (0) | 2021.01.07 |
[Stream API] 중간 연산 - flatMap 메서드 (0) | 2021.01.05 |
[Stream API] 중간 연산 - map 메서드 (2) | 2021.01.04 |
[Stream API] 중간 연산 - slicing 관련 메서드들 (0) | 2021.01.03 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- flatMapToInt
- #예제 #example #가계부 #Account Book
- mapToObj
- Stream API
- 토이 프로젝트
- 익명 클래스
- flatMapToLong
- 자바
- Java8
- java14
- 스트림
- #React #ReactJS #리액트
- 다짐
- 중간 연산
- jdk14
- flaMap
- flatMapToDouble
- 충북 콕! 콕!
- lambda
- java
- IntelliJ
- modern java
- 개발자
- import문
- 회고
- 목표
- 변경사항
- #배열 #array #map 함수
- 계획
- 람다
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함