스트림 소개
스트림(Stream)은 자바8부터 추가된 반복자이며, 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해준다.
반복자 스트림
자바 7 까지는 Iterator를 사용했지만, 자바 8에서는 Stream이란 것을 사용할 수 있다.
// iterator
List<String> list = Arrays.asList("콘성현", "강성현", "깡냉");
// Iterator
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String name = iterator.next();
System.out.println(name);
}
// stream
List<String> list = Arrays.asList("콘성현", "강성현", "깡냉");
// Stream
Stream<String> stream = list.stream();
stream.forEach(name -> System.out.println(name));
위의 두 코드는 같은 역할을 하는 코드이며, Stream API를 사용한 것이 훨씬 더 간단한 것을 볼 수 있다.
컬렉션(java.util.Collection)의 stream()
메서드로 스트림 객체를 얻고 나서
stream.forEach(name -> System.out.println(name)); 메소드를 통해 컬렉션의 요소를 하나씩 콘솔에 출력한다.
forEach()메소드는 다음과 같이 Consumer 함수적 인터페이스(Functional Interface)의 타입 매개변수를 가지므로 람다식으로 기술할 수 있다.
void forEach(Consumer<T> action);
자바스크립트가 익숙하다면 이와 같은 문법이 어렵지 않게 느껴질 수도 있으나, 자바만 해왔다면 조금 난해하게 느껴질 수도 있다.
함수형 언어인 자바스크립트는 이미 같은 문법을 지원한다.
var list = [1, 2, 3, 4, 5];
list.forEach(function(a){ console.log(a);});
스트림의 특징
Iterator와 Stream은 반복자로써 같은 역할을 가지지만, Stream 만의 특징이 있다.
이 특징에 대해 알아보자.
1. 람다식으로 요소 처리 코드를 제공한다.
Stream이 제공하는 요소 처리 메소드는 함수적 인터페이스 매개 타입(Functional Interface) 를 가지기 때문에 람다식
또는 메소드 참조
를 이용해서 요소 처리 내용을 매개값 으로 전달할 수 있다.
public class Worker {
private String name;
private int age;
public Worker(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class LamdaExpressionExample {
public static void main(String[] args) {
List<Worker> list = Arrays.asList(
new Worker("강성현", 30),
new Worker("깡냉", 20)
);
Stream<Worker> stream = list.stream();
stream.forEach(s -> {
String name = s.getName();
int age = s.getAge();
System.out.println(name + " - " + age);
});
}
}
// 강성현 - 30
// 깡냉 - 20
2. 내부 반복자를 사용하므로 병렬 처리가 쉽다.
외부 반복자(external iterator) -> 개발자가 코드로 직접 컬렉션의 요소를 반복해서 가져오는 코드 패턴
for(int i=0; i< length; i++){
int num = list.get(i); // 외부 반복자, collection에서 요소를 얻어와서 처리
// 처리
}
while(iter.hasNext()){
int num = iter.next(); // 외부 반복자
// 처리
}
내부 반복자(internal iterator)Nanum Gothic -> 컬렉션 내부에서 요소들을 반복시키고, 개발자는 요소당 처리해야할 코드만 제공하는 코드 패턴
내부 반복자 의 이점은 어떻게 요소를 반복시킬 것인가는 컬렉션에 맡겨두고, 개발자는 요소 처리 코드에만 집중할 수 있다.
- 내부 반복자는 요소들의 반복 순서를 변경하거나, 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있게 도와줌
- 순차적 외부 반복자보다 효율적으로 요소를 반복시킬 수 잇음
스트림
은 람다식으로 요소 처리 내용만 전달하고, 반복은 컬렉션 내부에서 일어난다.
=> 컬렉션 내부에서 병렬 처리가 가능하다.병렬(parallel)
처리란 한 가지 작업을 서브 작업으로 나누고, 서브 작업들을 분리된 스레드에서 병렬적으로 처리하는 것을 말한다.
public static void main(String[] args){
List<String> list = Arrays.asList("A군", "B군", "C군", "D군", "E군", "F군");
// 순차처리
Stream<String> stream = list.stream();
stream.forEach(ParallelExample :: print);
System.out.println();
// 병렬처리
Stream<String> parallelStream = list.parallelStream();
parallelStream.forEach(ParallelExample :: print);
}
public static void print(String str){
System.out.println(str + " : " + Thread.currentThread().getName());
}
A군 : main
B군 : main
C군 : main
D군 : main
E군 : main
F군 : main
D군 : main
F군 : main
E군 : ForkJoinPool.commonPool-worker-2
B군 : ForkJoinPool.commonPool-worker-9
C군 : ForkJoinPool.commonPool-worker-9
A군 : ForkJoinPool.commonPool-worker-11
위의 예제를 보면 parallelStream()으로 얻은 Stream은 ForkJoinPool(스레드풀)의 스레드들이 병렬적으로 요소를 처리하는 것을 볼 수 있다. (순서대로 실행되지 않는다.)
3. 스트림은 중간 처리와 최종 처리를 할 수 있다.
스트림은 순회를 하면서 요소의 중간 처리와 최종 처리를 할 수 있다.
중간 처리 => Intermediate Operation
매핑, 필터링, 정렬 등
최종 처리 => Terminal Operation
반복, 카운팅, 평균, 총합 등 (집계처리)
Worker 객체를 중간 처리에서 age값으로 매핑하고, 최종처리에서 age의 평균값을 산출한다.
public static void main(String[] args){
List<Worker> workerList = Arrays.asList(
new Worker("깡냉", 30),
new Worker("가뤵", 20),
new Worker("곡냉", 10)
);
double avg = workerList.stream()
.mapToInt(Worker :: getAge) // 중간처리 (age로 매핑)
.average() // 최종 처리 (평균)
.getAsDouble();
System.out.println("평균 점수 : " + avg);
}
// 평균 점수 : 20.0
[출처 : 이것이 자바다]
'프로그래밍 노트 > JAVA' 카테고리의 다른 글
[JAVA] Stream pipeline(스트림 파이프라인) (0) | 2019.06.17 |
---|---|
[JAVA] 스트림(Stream)의 종류 (0) | 2019.06.17 |
[JAVA] String 객체와 리터럴 (메모리관련) (2) | 2019.04.23 |
[JAVA] 익명객체_익명 구현 객체 생성 (0) | 2019.04.15 |
[JAVA] 익명객체_익명 자식 객체 생성 (0) | 2019.04.15 |