일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 백준
- 함수형 프로그래밍
- 겨울카카오인턴
- javscript
- design-pattern
- 자바
- Java
- lambda calculus
- 디자인패턴
- Pattern
- Eclipse
- 로버트마틴
- Network
- 프로그래머스
- JDBC
- Rails
- Collections
- JavaScript
- exception
- Python
- DesignPattern
- Spring
- 람다 칼큘러스
- 파이썬
- functional programming
- tcp
- solid
- Collection
- 스택
- 큐
- Today
- Total
개발자 노트
stream api - peek 본문
Stream API
peek?
문서에서 왜 Stream.peek을 주로 디버깅 용으로 사용하라고 했는가? 대하여 알아보겠습니다.
Stream API
peek은 Stream api의 한 부분이므로 Stream API 문서부터 살펴보도록 하겠습니다.
stream api의 문서를 보시면 filter
, map
등과 같은 method에 전달되는 함수는 올바른 동작을 위하여 다음 2가지 제약조건을 만족해야 한다고 합니다.
- non-interfering이여야 할 것
- 대부분의 경우, 상태를 지니지 말아야 할 것
여기서, non-interfering에 주목해보겠습니다
non-interfering
interfering의 사전적 의미
간섭하는, 참견하기 좋아하는
문서에서 말하는 non-interfering
stream pipeline이 실행되는 동안 data source를 조금도 수정하지 않아야 하는 것을 의미한다. - 문서 참조
이를 어길 경우
- 의도치 않은 결과가 발생하거나
- exception이 발생할 수 있다고 합니다.
위 지식을 바탕으로 java docs에 설명된 peek을 살펴보겠습니다.
Peek
여기서 2가지를 참조하시면 됩니다.
- peek에 전달되는 action parameter는 non-interfering action이어야 한다.
- peek은 intermediate operation이다.
다시 말해서 peek 연산에서 source data를 변경시키면 안 된다는 뜻입니다.
intermediate operation가 왜 중요한 지는 앞으로 예시를 보며 살펴보겠습니다.
예시
실제로 source data를 수정하는 지 그리고 peek의 의도치 않은 결과를 같이 살펴보겠습니다.
source data의 수정
코드
@Test
void test() {
List<Wrapper> origin = List.of(new Wrapper(1L), new Wrapper(2L));
List<Wrapper> modified = origin.stream()
.peek(h -> h.setA(4L))
.collect(Collectors.toList());
assertThat(origin).isNotEqualTo(modified);
}
public static class Wrapper {
private Long a;
public void setA(Long a) {this.a = a;}
public Wrapper(Long a) {this.a = a;}
@Override
public String toString() {
return "Wrapper{" +
"a=" + a +
'}';
}
}
결과
data source에 해당하는 origin list의 원소 값이 변경되었습니다.
peek과 count - 1
코드
아래 코드는 콘솔에 무엇이라 출력될까요?
@Test
void test2() {
long count = IntStream.range(1, 10)
.peek(System.out::println)
.count();
assertThat(count).isEqualTo(9);
}
결과
출력이 없습니다. java 9에서 terminal operation인 count의 최적화 때문입니다.
peek은 count의 갯수에 영향을 주지 않으므로 표준출력하는 함수가 실행되지 않았습니다.
즉, intermediate operation은 최적화를 위해 실행되지 않을 수 있습니다.(lazy evaluation 참조)
peek과 count - 2
코드
@Test
void test3() {
long count = IntStream.range(1, 10)
.filter(n -> n % 2 == 0)
.peek(System.out::println)
.count();
assertThat(count).isEqualTo(4);
}
결과
filter는 count의 결과에 영향을 주는 intermediate operation이므로 이때 peek은 평가가 됩니다.
주의
따라서 개발을 할 때 peek 사용시 주의할 점을 정리하자면 다음과 같습니다.
- peek은 intermediate operation이기 때문에 terminal operation에 따라 평가되지 않을 수 있음. (lazy evaluation으로 최적화)
- document의 설명과 다르게 사용하므로 추후 버전 업 시 문제될 수 있음.
- ex:) java 8 → java 9에서 count()의 최적화
마지막으로 서로 다른 패러다임을 지닌 메서드를 살펴보고 마무리를 짓겠습니다.
Stream.forEach와 Iterable.forEach
동일한 메서드 명이지만, 함수형 패러다임으로 만들어진 Stream.forEach 그리고,
절차지향 패러다임으로 만들어진 Iterable.forEach가 어떻게 설명이 다른 지 살펴보겠습니다.
Stream.forEach
도 동일하게 non-interfering action을 전달하라고 명시되어 있는 반면, - 링크
Iterable.forEach
는 source element를 수정하면 side-effect가 발생할 수 있다고 주의만 작성되어 있습니다. - 링크
고민 - 표현
peek과 forEach는 반드시 side-effect를 주어 의도치 않은 결과 발생할 수 있음
- 표준 출력조차 사이드 이펙트로 정의. side-effect 중 data source를 수정하지 않는 non-interfering action을 구현해야 한다는 뜻으로 해석 가능.
'컴퓨터 언어 > Java' 카테고리의 다른 글
Collection 정리 - Set Interface (2) | 2022.07.14 |
---|---|
Collection 정리 - Collection Interface (0) | 2022.07.06 |
Collection 정리 - Implementations (0) | 2022.07.04 |
Collections정리 - Interfaces (0) | 2022.06.30 |
Collection 정리 (0) | 2022.06.30 |