스트림 : 데이터 원소의 유한 혹은 무한 시퀀스
스트림 파이프 라인 : 이 원소들로 수행하는 연산 단계를 표현
외부 반복자
컬렉션의 요소를 가져오는 것에서 처리하는 것까지 모두 개발자가 작성
ex) for, while-iterator
내부 반복자
어떻게 요소를 반복시킬 것인가는 컬렉션 내부 에서 일어난다
개발자는 요소당 처리할 코드에만 집중 할 수 있다.
병렬처리가 컬렉션 내부에서 처리되므로 간편함
// �순서
(소스 스트림 ) -> (중간 연산 ) -> (종단 연산 )
중간 연산은 스트림을 어떠한 방식으로 변환한다
종단 연산은 중간 연산의 결과물에 최종 연산을 한다
ex) 합계, 평균, 카운팅, 최대값, 최소, 컬렉션 담기, 특정원소선택 ...
종단 연산이 시작되기 전까지 중간 연산는 지연(lazy)된다
종단 연산이 시작되면 컬렉션 요소가 중간 스트림에서 처리되고 종단연산까지 오게됨
스트림을 과용하면 읽기 어렵고 유지보수가 힘들다.
public static void main (String [] args ) throws IOException {
Path dictionary = Paths .get (args [0 ]);
int minGroupSize = Integer .parseInt (args [1 ]);
try (Stream <String > words = Files .lines (dictionary )) {
words .collect (
groupingBy (word -> word .chars ().sorted ()
.collect (StringBuilder ::new ,
(sb , c ) -> sb .append ((char ) c ),
StringBuilder ::append ).toString ()))
.values ().stream ()
.filter (group -> group .size () >= minGroupSize )
.map (group -> group .size () + ": " + group )
.forEach (System .out ::println );
}
}
적절히 활용하면 위 예시와 같은 동작을 하는데 깔끔하고 명료해진다.
public static void main (String [] args ) throws IOException {
Path dictionary = Paths .get (args [0 ]);
int minGroupSize = Integer .parseInt (args [1 ]);
try (Stream <String > words = Files .lines (dictionary )) {
words .collect (groupingBy (word -> alphabetize (word )))
.values ().stream ()
.filter (group -> group .size () >= minGroupSize )
.forEach (g -> System .out .println (g .size () + ": " + g ));
}
}
private static String alphabetize (String s ) {
char [] a = s .toCharArray ();
Arrays .sort (a );
return new String (a );
}
1. 람다에서는 매개변수 이름을 잘 지어야 가독성이 유지된다.
ex) 위 예시에서 스트림 변수 이름을 words로 지어 명확히함
2. 스트림 파이프라인에서 도우미 메서드를 적절히 활용하는건 중요하다.
연산에 적절한 이름을 지어주고 세부 구현을 주 프로그램 로직 밖으로 빼내서 가독성을 높이자.
ex) 위 예시에서 단어 철자를 알파벳순으로 정렬하는 일을 별도 메서드인 alphabetize
3. char 값을 처리할때는 스트림을 삼가하는 편이 낫다.
자바가 char용 스트림을 지원하지 않아 잘못 구현할 가능성 크다.
ex) 위 예시 alphabetize 메서드
기존 코드는 스트림을 사용하도록 리팩터링하되, 새 코드가 더 나아보일 때만 반영하자.
스트림으로 바꾸는게 가능해도 가독성, 유지보수성이 안좋아 질수도 있기떄문
앞 예시처럼 스트림, 반복문 적절히 조합하는게 최선
스트림의 함수 객체로는 할 수 없지만 반복 코드의 코드블록로 할 수있는 일이 있다.
람다에서는 final인 변수만 읽을 수 있다.
람다에서는 return, break, continue 문 사용할 수 없다.
원소들의 시퀀스를 일관성 있게 변환할 때
원소들의 시퀀스를 필터링할 때
원소들의 시퀀스를 연산 후 결합할 때
원소들의 시퀀스를 모을 때
원소들의 시퀀스 중 특정 조건을 만족하는 원소를 찾을 때
원본 값을 계속 써야할 때
스트림은 중간 연산을 지나서 다른 값에 매핑되면 원래 값을 잃는 구조다.
파이프라인의 순서를 바꿈으로써 해결할 수 있는지 고민해보자.
스트림 또는 반복이 좋은 경우가 있다. (많은 경우는 조합하는 경우가 낫다)
스트림과 반복 중 어느 쪽이 나은지 확신하기 어렵다면 둘 다 해보고 더 나은 쪽을 선택하자.