스트림 요소를 수정하기 위해 peek ()를 사용하는 것이 반 패턴입니까?


36

내가 스트림을 가지고 있고 스트림 중간에 "풍부하게"하고 싶다고 가정 해 보겠습니다 peek(). 예를 들면 다음과 같습니다.

streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

코드에서이 시점에서 Things를 변경하는 것이 올바른 동작이라고 가정합니다. 예를 들어, thingMutator메소드는 "lastProcessed"필드를 현재 시간으로 설정할 수 있습니다.

그러나 peek()대부분의 상황에서 "보이지만 만지지 마십시오"를 의미합니다.

스트림 요소 peek()음소거 하는 데 사용 하는 것이 반 패턴이거나 잘못 권장됩니까?

편집하다:

보다 전통적인 대안은 소비자를 전환하는 것입니다.

private void thingMutator(Thing thing) {
    thing.setLastProcessed(System.currentTimeMillis());
}

매개 변수를 반환하는 함수로

private Thing thingMutator(Thing thing) {
    thing.setLastProcessed(currentTimeMillis());
    return thing;
}

사용이 map()대신 :

stream.map(this::thingMutator)...

그러나 그것은 perfunctory 코드 ( return) 를 소개 peek()하며 동일한 객체를 반환한다는 것을 알고 있기 때문에 더 명확하다고 확신 하지는 않지만 map()동일한 클래스 의 객체 라는 것을 한눈에 알 수는 없습니다 .

또한, peek()당신은 돌연변이 람다를 가질 수 있지만 map(), 기차 난파선을 만들어야합니다. 비교:

stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)

나는 peek()버전이 더 깨끗하고 람다는 분명히 변하고 있다고 생각 하므로 "신비한"부작용은 없다. 유사하게, 방법 참조가 사용되고 방법의 이름이 돌연변이를 분명히 암시한다면, 그것도 분명하고 명백하다.

개인적으로, 나는 peek()돌연변이 를 사용하여 부끄러워하지 않습니다 . 매우 편리합니다.



1
peek요소를 동적으로 생성하는 스트림과 함께 사용 했습니까 ? 여전히 작동합니까, 아니면 변경 사항이 손실됩니까? 스트림 요소를 수정하면 신뢰할 수없는 소리가납니다.
Sebastian Redl

@SebastianRedl 100 % 신뢰할 수 있음을 발견했습니다. 처리 중에 수집하기 위해 처음 사용했습니다. List<Thing> list; things.stream().peek(list::add).forEach(...);매우 편리합니다. 최근. 게시 정보를 추가하는 데 사용했습니다 : Map<Thing, Long> timestamps = ...; return things.stream().peek(t -> t.setTimestamp(timestamp.get(t))).collect(toList());. 이 예제를 수행하는 다른 방법이 있다는 것을 알고 있지만 여기서는 지나치게 단순화하고 있습니다. 를 사용 peek()하면 더 작고 더 우아한 코드 IMHO가 생성됩니다. 가독성을 제외하고,이 질문은 실제로 당신이 제기 한 것에 관한 것입니다. 안전 / 신뢰할 수 있습니까?
보헤미안


@보헤미아 사람. 아직도이 방법을 연습하고 peek있습니까? 나는 stackoverflow에 대해 비슷한 질문을 가지고 있으며 그것을보고 피드백을 줄 수 있기를 바랍니다. 감사. stackoverflow.com/questions/47356992/…
tsolakp

답변:


23

영어 단어의 의미에서 "피킹"은 "보이지만 만지지 마십시오"를 의미합니다.

그러나 의 JavaDoc의 상태 :

몰래 엿보다

스트림 엿보기 (소비자 행동)

이 스트림의 요소로 구성된 스트림을 리턴하고 결과 스트림에서 요소가 소비 될 때 각 요소에 대해 제공된 조치를 추가로 수행합니다.

핵심 단어 : "수행 중 ... 작업"및 "소비". JavaDoc은 peek스트림을 수정할 수있는 능력이 있어야한다는 점을 분명히 알고 있습니다.

그러나 JavaDoc은 또한 다음과 같이 말합니다.

API 참고 사항 :

이 방법은 주로 디버깅을 지원하기 위해 존재하며 파이프 라인의 특정 지점을지나 가면서 요소를 확인하려는 경우

이것은 예를 들어 스트림의 요소 로깅과 같은 관찰을위한 것임을 나타냅니다.

이 모든 것에서 취한 것은 스트림의 요소를 사용 하여 작업 수행 할 수 있지만 스트림의 요소를 변경 하지 않아야한다는 것 입니다. 예를 들어, 객체에 대해 메소드를 호출하지만 객체에 대한 조작을 피하십시오.

최소한 다음 줄을 따라 코드에 간단한 주석을 추가합니다.

// Note: this peek() call will modify the Things in the stream.
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

이러한 의견의 유용성에 대한 의견은 다르지만이 경우 그러한 의견을 사용합니다.


1
메소드 이름이 분명하게 돌연변이, 예를 들어 신호를하면 왜 코멘트를 필요합니까 thingMutator, 또는보다 구체적인 resetLastProcessed설득력있는 이유가없는 한, 필요 코멘트 등을 귀하의 제안은 일반적으로 변수 및 / 또는 메소드 이름의 빈약 한 선택을 표시 등. 좋은 이름을 선택하면 충분하지 않습니까? 또는 좋은 이름에도 불구하고 peek()대부분의 프로그래머가 "탈지"(시각적으로 건너 뛰는) 사각 지대와 같은 것이 있습니까? 또한 "주로 디버깅"은 "디버깅에만"과 같은 것이 아닙니다. "주로"이외의 용도는 무엇입니까?
보헤미안

@Bohemian 메소드 이름이 직관적이지 않기 때문에 주로 설명하겠습니다. 그리고 나는 Oracle에서 일하지 않습니다. 나는 그들의 자바 팀이 아닙니다. 나는 그들이 무엇을 의도했는지 전혀 모른다.

@Bohemian 왜냐하면 내가 싫어하는 방식으로 요소를 수정하는 것을 찾을 때 "peek"은 아무것도 변경하지 않기 때문에 "peek"에서 줄 읽기를 중단 할 것이기 때문입니다. 나중에 다른 코드의 모든 장소에 내가 쫓고있는 버그가 포함되어 있지 않은 경우에만 디버거로 무장 한 다음 "omg, 누가이 잘못된 코드를 넣었습니까?"
Frank Hopkins

예를 들어 @Bohemian은 여기서 모든 것이 한 줄에 있고 어쨌든 읽을 필요가 있지만 "peek"의 내용을 건너 뛸 수 있으며 종종 한 줄 당 한 번의 스트림 작업으로 여러 줄을 통해 스트림 문이
나옵니다.

1

쉽게 잘못 해석 될 수 있으므로 이와 같이 사용하지 않는 것이 좋습니다. 아마도 가장 좋은 옵션은 람다 함수를 사용하여 필요한 두 동작을 forEach 호출에 병합하는 것입니다. 기존 객체를 변경하지 않고 새 객체를 반환하는 것을 고려할 수도 있습니다. 약간 덜 효율적이지만 더 읽기 쉬운 코드가 생성 될 수 있으며 수정 된 목록을 사용해야하는 다른 작업에 실수로 수정 ​​된 목록을 사용할 가능성이 줄어 듭니다. 원본을 받았습니다.


-2

API 노트는이 메소드가 디버깅 / 로깅 / 발사 통계 등과 같은 작업에 주로 추가되었음을 알려줍니다.

@apiNote이 방법은 주로 디버깅을 지원하기 위해 존재합니다. 여기서 파이프 라인의 특정 지점을지나 가면서 요소를 볼 수 있습니다. *

       {@암호
     * Stream.of ( "1", "2", "3", "4")
     * .filter (e-> e.length ()> 3)
     * .peek (e-> System.out.println ( "필터링 된 값 :"+ e))
     * .map (문자열 :: toUpperCase)
     * .peek (e-> System.out.println ( "매핑 된 값 :"+ e))
     * .collect (Collectors.toList ());
     *}


2
당신의 대답은 이미 눈사람에 의해 덮여 있습니다.
Vincent Savard 17

3
"주로"가 "전용"을 의미하지는 않습니다.
보헤미안

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.