Stream
자바 8부터 추가된 컬렉션의 저장요소를 하나씩 참조해서 람다식(functional style)으로 처리할 수 있도록 해주는 반복자
iterator -> stream
iterator 반복자를 사용했을 때
1
2
3
4
5
6
List<String> list = Arrays.asList("김", "나", "박", "이");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String name = iterator.next();
System.out.println(name);
}
stream을 사용했을 때
1
2
3
List<String> list = Arrays.asList("김", "나", "박", "이");
Stream<String> stream = list.stream();
stream.forEach(name -> System.out.println(name));
스트림의 특징
iterator와 차이점
- 람다식으로 요소 처리 코드를 제공한다
- 내부 반복자를 사용해 병렬 처리가 쉽다
- 중간 처리와 최종 처리 작업을 수행한다
1. 람다식으로 요소 처리 코드를 제공
- 스트림에서는 람다식 또는 메소드 참조를 이용해 ㅇ내용을 매개변수로 전달할 수 있다
1
2
3
stream.forEach(s -> {
System.out.println(s.getName()); // list의 각각의 item을 s로 부르고 매개변수로 전달한 것
})
2. 내부 반복자를 사용해 병렬 처리
- 외부 반복자
external iterator
: 개발자가 코드로 직접 컬렉션 요소를 반복해서 가져오는 코드 패턴- index를 이용하는 for문, while문 등
(출처: https://stackoverflow.com/questions/224648/external-iterator-vs-internal-iterator)
- 내부 반복자의 장점
- 개발자가 컬렉션 요소 반복 처리를 직접 작성하지 않아도 됨(스트림이 알아서 하니까)
- 요소들의 반복 순서를 변경하거나, 멀티코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있도록 도와주기 때문에 하나씩 처리하는 순차적 외부 반복자보다 효율적으로 요소를 반복시킬 수 있다
- 병렬 처리
parallel
: 한가지 작업을 서브 작업으로 나누고, 서브 작업들을 분리된 스레드에서 병렬적으로 처리하는 것- 런타임시 하나의 작업을 서브작업으로 자동으로 나눈 뒤, 서브 작업 결과를 자동으로 결합해서 최종 결과물을 생성한다
3. 중간 처리와 최종 처리 작업 수행
- 스트림은 컬렉션 요소에 대해 중간 처리와 최종 처리를 수행할 수 있다
- 중간 처리 : 매핑, 필터링, 정렬 수행
- 최종 처리 : 반복, 카운팅, 평균, 총합
(출처: https://strange-developer.tistory.com/15)
스트림의 종류
BaseStream
인터페이스에 모든 스트림에서 사용할 수 있는 공통 메소드들이 정의되어있다.- 하위 스트림으로
Stream
,IntStream
,LongStream
,DoubleStream
이 있다. Stream
: 객체 요소를 처리하는 스트림IntStream
: int요소를 처리하는 스트림
메소드 예시
리턴타입 | 메소드(매개변수) | 소스 |
---|---|---|
Stream<T> |
- java.util.Collection.stream() - java.util.Collection.parallelStream() |
컬렉션 |
Stream<Path> |
- Files.find(Path, int, BiPredicate, FileVisitOption) - Files.list(Path) |
디렉토리 |
Stream<String> |
- Files.lines(Path, Charset) - BufferedReader.lines() |
파일 |
컬렉션으로부터 스트림 얻기
리스트이름.stream()
배열로부터 스트림 얻기
Arrays.stream(배열이름)
숫자 범위로부터 스트림 얻기
IntStream.rangeClosed(1, 100).forEach(a -> sum += a)
: 1부터 100까지 합 구하기
파일로부터 스트림 얻기
Path path = Paths.get("src/example.txt")
: 파일 경로 정보를 가지고 있는 Path객체 생성Stream<String> stream = Files.lines(path, Charset.defaultCharset())
: 운영체제의 기본 문자셋- BufferedReader의 lines()메소드 이용
1
2
3
4
5
File file = path.toFile();
FileReader fileReader = new FileReader(file);
BufferedREader br = new BufferedREader(fileReader);
stream = br.lines();
stream.forEach(System.out :: println);
디렉토리로부터 스트림 얻기
- Files의 정적 메소드 list()이용해 디렉토리의 내용을 스트림으로 읽고 콘솔에 출력하는 예제
1
2
3
4
5
Path path = Paths.get("C:/Users/src");
Stream<Path> stream = Files.list(path);
stream.forEach(p -> System.out.println(p.getFileName()));
// P : 서브 디렉토리 또는 파일에 해당하는 Path 객체
// p.getFileName() : 서브 디렉토리 이름 또는 파일 이름 리턴
스트림 파이프라인
- 리덕션
Reduction
: 대량의 데이터를 가공해서 축소하는 것 - 리덕션의 결과물: 데이터의 합계, 평균값, 카운팅, 최대값, 최소값
- 컬렉션 요소를 리덕션 결과물로 바로 집계할 수 없을 경우, 집계하기 좋도록 필터링, 매핑, 정렬, 그룹핑 등의 중간 처리가 필요
중간 처리와 최종 처리
- 파이프라인
piplelines
: 여러 개의 스트림이 연결되어 있는 구조 - 스트림은 중간처리와 최종처리를 파이프라인으로 해결한다
- 파이프라인에서 최종 처리를 제외하고는 모두 중간 처리 스트림이다
- 중간 스트림이 생성될 때 요소들이 바로 중간처리 (필터링, 매핑, 정렬)되는 것이 아니라
최종 처리가 시작되기 전까지 중간 처리는 지연된다(lazy) - 최종 처리가 시작되면 비로소 컬렉션의 요소가 하나씩 중간 스트림에서 처리되고 최종처리까지 오게 된다