1. 스트림(Stream)이란?
- 컬렉션(배열 포함)의 저장 요소를 하나씩 참조해서 람다식(함수적-스타일(functional-style))으로 처리할 수 있도록 해주는 반복자.
2. 반복자 스트림
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
public class IteratorVsStreamExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("홍","신","김");
//Iterator 이용
Iterator<String> iter = list.iterator();
while(iter.hasNext()) {
String name = iter.next();
System.out.println(name);
}
System.out.println();
//Stream 이용
Stream<String> stream = list.stream();
stream.forEach( name -> System.out.println(name) );
}
}
3. 스트림의 특징
1) 람다식으로 요소 처리 코드를 제공함.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class LambdaExpressionsExample {
public static void main(String[] args) {
List<Student> list = Arrays.asList(
new Student("홍", 90),
new Student("신", 92)
);
Stream<Student> stream = list.stream();
stream.forEach( s-> {
String name = s.getName();
int score = s.getScore();
System.out.println(name + "-" + score);
});
}
}
2) 내부 반복자를 사용하므로 병렬 처리가 쉬움.
- 외부 반복자 :
index
를 이용하는for문
,Iterator
를 이용하는while문
- 내부 반복자 : 컬렉션 내부에서 요소들을 반복시키고, 개발자는 요소당 처리해야 할 코드만 제공하는 코드 패턴
- 내부 반복자는 요소들의 반복 순서를 변경하거나, 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있게 도와주기 때문에 하나씩 처리하는 순차적 외부 반복자보다는 효율적으로 요소를 반복시킬 수 있음.
- 스트림은 람다식으로 요소 처리 내용만 전달할 뿐, 반복은 컬렉션 내부에서 일어남.
* 병렬(parallel) 처리란?
- 한 가지 작업을 서브 작업으로 나누고, 서브 작업들을 분리된 스레드에서 병렬적으로 처리하는 것.
- 병렬 처리 스트림을 이용하면 런타임시 하나의 작업을 서브 작업으로 자동으로 나누고, 서브 작업의 결과를 자동으로 결합해서 최종 결과물을 생성함.
- 병렬 처리 스트림 선언 예시 :
List<String> list = new ArrayList<>();
Stream<String> parallelStream = list.parallelStream();
3) 스트림은 중간 처리와 최종 처리를 할 수 있음.
- 중간 처리 : 매핑, 필터링, 정렬 수행
- 최종 처리 : 반복, 카운팅, 평균, 총합 등 집계 처리 수행
- 예시
- 학생 객체를 요소로 가지는 컬렉션이 있다고 가정.
- 중간 처리 : 학생의 점수를 뽑아냄(Student 객체를 점수로 매핑)
- 최종 처리 : 점수의 평균값을 산출함(집계)
예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Arrays;
import java.util.List;
public class MapAndReduceExample {
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
new Student("홍", 10),
new Student("신", 20),
new Student("유", 30)
);
double avg = studentList.stream()
// 중간 처리(학생 객체를 점수로 매핑
.mapToInt(Student :: getScore)
// 최종 처리(평균 점수)
.average()
.getAsDouble();
System.out.println("평균 점수 = " + avg);
}
}
4. 스트림의 종류
Stream
: 객체 요소 처리IntStream
,LongStream
,DoubleStream
: 각각int
,long
,double
요소 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamExample {
public static int sum;
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
new Student("홍", 10),
new Student("신", 20),
new Student("유", 30)
);
// 컬렉션으로부터 스트림 얻기
Stream<Student> stream = studentList.stream();
stream.forEach( s-> System.out.println(s.getName()) );
System.out.println("=======================================");
String[] setArray = {"홍","신","유"};
// 배열로부터 스트림 얻기
Stream<String> strStream = Arrays.stream(setArray);
strStream.forEach( str -> System.out.println(str) );
System.out.println("=======================================");
// 정수 범위를 소스로 하는 스트림
// rangeClosed() : 첫 번째 매개값에서부터 두 번째 매개값까지 순차적으로 제공하는 IntStream을 리턴
IntStream intStream = IntStream.rangeClosed(1, 100);
intStream.forEach( n -> sum += n);
System.out.println("총합: " + sum);
}
}
5. 스트림 파이프라인
- 리덕션(
Reduction
) :- 대량의 데이터를 가공해서 축소하는 것.
- ex) 데이터의 합계, 평균값, 카운팅, 최대값, 최소값 등이 리덕션의 결과물
- 파이프라인(
pipelines
) :- 여러 개의 스트림이 연결되어 있는 구조.
- 스트림은 중간 처리와 최종 처리를 파이프라인으로 해결함.
Stream
인터페이스에는 많은 중간 처리 메서드가 있는데, 이 메서드들은 중간 처리된 스트림을 리턴하며, 이 스트림에서 다시 중간 처리 메서드를 호출해서 파이프라인을 형성하게 됨.
남자 평균 나이 구하기 예제 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Member {
public static int MALE = 0;
public static int FEMALE = 1;
private String name;
private int sex;
private int age;
public Member(String name, int sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public int getSex() {
return sex;
}
public int getAge() {
return age;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.Arrays;
import java.util.List;
public class StreamPipelinesExample {
public static void main(String[] args) {
List<Member> list = Arrays.asList(
new Member("홍", Member.MALE, 30),
new Member("김", Member.FEMALE, 20),
new Member("신", Member.MALE, 45),
new Member("박", Member.FEMALE, 27)
);
// 남자 평균 나이 구하기
double ageAvg = list.stream()
.filter( m -> m.getSex()==Member.MALE )
.mapToInt(Member :: getAge)
.average()
.getAsDouble();
System.out.println("남자 평균 나이 : " + ageAvg);
}
}