Java8 에서 stream 에는 두가지의 반복문이 사용가능한데
peek() 과 forEach() 가 있다.
두가지의 차이점이라고하면 forEach 는 그자체만 사용가능하지만 peek() 은 그렇지 않다.
이유는 단순한데 forEach는 return 값이 void 라서 최종처리메소드로 쓰일 수 있지만 peek은 stream 을 return 해서 불가능하다.
/*peek 구현체*/
@Override
public final Stream<P_OUT> peek(Consumer<? super P_OUT> action) {
Objects.requireNonNull(action);
return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
0) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
@Override
public void accept(P_OUT u) {
action.accept(u);
downstream.accept(u);
}
};
}
};
}
/*forEach 구현체*/
@Override
public void forEach(Consumer<? super P_OUT> action) {
evaluate(ForEachOps.makeRef(action, false));
}
그래서 peek() 은 최종처리 메소드가 필요한데 sum 같은 최종처리 메소드가 필요합니다. 이 때 sum의 위치에 올 수 있는 것 중 하나가 바로
boolean 값을 리턴해주는 allMatch(), noneMatch(), anyMatch() 같은 것들 인데
이 과정에서 peek() 내부에 절대 상태변화가 들어가는 코드를 작성하면 안된다.
예를 들어
class Member {
String name;
Integer age;
public Member(String name, Integer age) {
this.name = name;
this.age = age;
}
public boolean equals(Member member) {
return (this.name.equals(member.name) && this.age.equals(member.age));
}
}
public void peekTest() {
Member member1 = new Member("MuMu", 30);
Member member2 = new Member("MuNu", 40);
Member member3 = new Member("MuLu", 50);
List<Member> members = Lists.newArrayList(member1, member2, member3);
System.out.println("TEST 1");
members.stream()
.peek(m ->
System.out.println("My name is " + m.name))
.anyMatch(m -> m.equals(member1));
System.out.println("TEST 2");
members.stream()
.peek(m ->
System.out.println("My name is " + m.name))
.anyMatch(m -> m.equals(member2));
System.out.println("TEST 3");
members.stream()
.peek(m ->
System.out.println("My name is " + m.name))
.anyMatch(m -> m.equals(member3));
}
이런 코드가 있다고 했을 때 다음과 같은 결과가 나온다.
TEST 1
My name is MuMu
My name is MuNu
TEST 2
My name is MuMu
TEST 3
My name is MuMu
forEach 와는 다르게 peek() 은 최종 처리 구문에 따라 실행순서를 조절한다. 모든 element 에 적용되는 것이아니라 Match 관련 메소드는 중간에 하나라도 결과값이 false가 되면 이 이후를 수행하지 않는다.
따라서 절대 peek 에는 처리 대상의 상태가 변경될 수 있는 코드를 넣지 않아야 한다.
물론 이외에도 stream 사용 중에 원본 collection 의 데이터를 변경하는 코드는 분명 좋지 않은 코드이다. 실제로 match 같은 메소드가 없더라도 peek에 remove 같은 액션을 해보면 다 사라지지 않는 것을 알 수 있다. 그러니까 Stream 내에서 원본 list의 상태를 변경하지말자
끝!
'Java' 카테고리의 다른 글
[OOP] 객체, 추상화, 캡슐화 (0) | 2019.12.14 |
---|---|
[의존성과 아키텍처] 조영호작가님의 우아한 객체지향 강의 후기 및 정리 (1) | 2019.12.09 |
[Java] Handlebars.java 써서 예약어 Template 으로 처리하기 (1) | 2018.12.31 |
[Java] Jackson으로 Json <-> Object 변환(Transformation)하기 (0) | 2018.06.28 |
[Java] .war .jar (0) | 2018.03.13 |