# Table of Contents
# 스트림
Java 8에서 추가된 스트림(Stream)
을 사용하면 Collection을 더욱 쉽게 순회, 필터링, 변환할 수 있다. 특히 람다식과 함께 사용하면 코드가 더욱 간결해진다.
간단한 예제를 살펴보면서 스트림이 어떤 것인지 알아보자. 1에서 6까지의 숫자가 List에 들어있다.
List<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.add(6);
이제 반복문을 사용하여 짝수만 추출해보자.
List<Integer> evenNumbers = new ArrayList<Integer>();
for (int i=0; i<numbers.size(); i++) {
if (numbers.get(i) % 2 == 0) {
evenNumbers.add(numbers.get(i));
}
}
System.out.println(evenNumbers.toString()); // [2, 4, 6]
스트림(Stream)
을 사용하면 더욱 쉽게 짝수를 추출할 수 있다.
List<Integer> evenNumbers = numbers.stream()
.filter(value -> value%2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers.toString()); // [2, 4, 6]
# 스트림 생성하기
다양한 방법으로 스트림을 생성할 수 있다.
# Stream.builder()
Builder
를 사용하면 스트림에 값을 직접 넣을 수 있다.
Stream<Integer> stream = Stream.<Integer>builder()
.add(1)
.add(2)
.add(3)
.add(4)
.build();
stream
.forEach(System.out::println);
1
2
3
4
# Stream.generate()
Stream.generate()
을 사용하면 람다식으로 스트림을 생성할 수 있다. generate()
만 사용하면 요소를 무한히 발행하며, limit()
메소드로 발행되는 요소의 수를 제한할 수 있다.
Stream<Integer> stream = Stream.generate(() -> 1).limit(5);
stream
.forEach(System.out::println);
1
1
1
1
1
# Stream.iterate()
Stream.iterate()
은 초기값과 람다식을 이용하여 스트림을 생성한다.
Stream<Integer> stream = Stream.iterate(100, x -> x+1).limit(5);
stream
.forEach(System.out::println);
100
101
102
103
104
# Stream.of()
Stream.of()
은 매개변수로 전달된 요소들로 스트림을 생성한다.
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
stream
.forEach(System.out::println);
1
2
3
4
5
# Stream.empty()
Stream.empty()
을 사용하면 빈 스트림을 생성할 수 있다.
Stream stream = Stream.empty();
stream
.forEach(System.out::println);
# Arrays.stream()
Arrays.stream()
을 사용하면 배열로 스트림을 만들 수 있다.
Integer[] array = {1, 2, 3, 4, 5};
Stream<Integer> stream = Arrays.stream(array);
stream
.forEach(System.out::println);
1
2
3
4
5
# List.stream()
List
로 스트림을 생성할 수 있다.
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Stream<Integer> stream = list.stream();
stream
.forEach(System.out::println);
1
2
3
4
# Set.stream()
Set
로 스트림을 생성할 수 있다.
Set<Integer> set = new HashSet<Integer>();
set.add(1);
set.add(2);
set.add(3);
set.add(3);
Stream<Integer> stream = set.stream();
stream
.forEach(System.out::println);
1
2
3
# 원시 타입 스트림
Wrapper 클래스와 제너릭을 사용하면 원시 타입의 스트림을 생성할 수 있다.
Stream<Integer> stream = Stream.<Integer>of(1, 2, 3, 4, 5);
stream
.forEach(System.out::println);
1
2
3
4
5
그러나 Wrapper 클래스를 사용하면 빈번한 오토박싱, 언박싱이 발생한다. 따라서 Stream API는 원시 타입을 위한 스트림을 제공한다.
- IntStream
- LongStream
- DoubleStream
원시 타입의 스트림은 다음과 같이 사용한다.
IntStream stream = IntStream.of(1, 2, 3, 4, 5);
stream
.forEach(System.out::println);
1
2
3
4
5
# 스트림 조작하기
# Stream.concat()
Stream.concat()
을 사용하면 두 스트림을 합쳐 새로운 스트림을 만들 수 있다.
Stream<Integer> stream1 = Stream.<Integer>of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = Stream.<Integer>of(6, 7, 8, 9, 10);
Stream<Integer> stream = Stream.concat(stream1, stream2);
stream
.forEach(System.out::println);
1
2
3
4
5
6
7
8
9
10
# Stream.filter()
Stream.filter()
을 사용하면 요소를 필터링할 수 있다. 아래 예제는 짝수만 필터링한다.
Stream<Integer> stream = Stream.<Integer>of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.filter(value -> value%2 == 0);
stream
.forEach(System.out::println);
2
4
6
8
10
# Stream.map()
Stream.map()
을 사용하면 요소들을 다른 값들로 매핑할 수 있다.
Stream<String> stream = Stream.of("Paul", "Monica", "Rachel", "Chandler")
.map(value -> value.toUpperCase());
stream
.forEach(System.out::println);
PAUL
MONICA
RACHEL
CHANDLER
# Stream.flatMap()
Stream.flatMap()
을 사용하면 요소를 다른 스트림으로 만들 수 있다.
Stream<Integer> stream = Stream.of(
new Student("Joey", 80, 90, 70),
new Student("Monica", 75, 100, 90),
new Student("Ross", 70, 80, 100)
).flatMap(student -> Stream.<Integer>of(student.getMath(), student.getEnglish(), student.getScience()));
stream
.forEach(System.out::println);
80
90
70
75
100
90
70
80
100
# Stream.sorted()
Stream.sorted()
을 사용하면 요소들을 정렬할 수 있다. 기본값은 오름차순이다.
Stream<Integer> stream = Stream.of(2, 3, 1, 5, 6, 4)
.sorted();
stream
.forEach(System.out::println);
1
2
3
4
5
6
역순으로 정렬할 수도 있다.
Stream<Integer> stream = Stream.of(2, 3, 1, 5, 6, 4)
.sorted(Collections.reverseOrder());
stream
.forEach(System.out::println);
6
5
4
3
2
1
Comparator
또는 Comparable
과 함께 사용할 수도 있다.
Stream<Person> stream = Stream.of(new Person("Paul", 35), new Person("Smith", 25), new Person("Jane", 15))
.sorted(new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
});
stream
.forEach(System.out::println);
# 스트림으로 결과 만들기
# Stream.foreach()
Stream.foreach()
을 사용하면 모든 요소들을 순회할 수 있다.
Stream<Integer> stream = Stream.of(2, 3, 1, 5, 6, 4);
stream
.forEach(System.out::println);
2
3
1
5
6
4
# Stream.count()
요소의 개수를 구할 수 있다.
Stream<Integer> stream = Stream.of(2, 3, 1, 5, 6, 4);
long count = stream.count(); // 6
# Stream.sum()
요소의 합을 구할 수 있다.
IntStream stream = IntStream.of(2, 3, 1, 5, 6, 4);
long count = stream.sum(); // 21
# Stream.min()
최소값을 찾을 수 있다.
IntStream stream = IntStream.of(2, 3, 1, 5, 6, 4);
OptionalInt min = stream.min();
min.ifPresent(System.out::println); // 1
# Stream.max()
최대값을 찾을 수 있다.
IntStream stream = IntStream.of(2, 3, 1, 5, 6, 4);
OptionalInt max = stream.max();
max.ifPresent(System.out::println); // 6
# Stream.average()
평균값을 찾을 수 있다.
IntStream stream = IntStream.of(2, 3, 1, 5, 6, 4);
OptionalDouble average = stream.average();
average.ifPresent(System.out::println); // 3.5
# Stream.reduce()
Stream.reduce()
는 각 요소에 람다식을 실행하여 단 1개의 결과를 반환한다.
IntStream stream = IntStream.of(2, 3, 1, 5, 6, 4);
OptionalInt sum = stream.reduce((acc, next) -> acc + next);
sum.ifPresent(System.out::println); // 21
# collect()와 Collectors
collect()
메소드는 데이터의 중간 처리 후 마지막에 원하는 형태로 변환해준다. 이 메소드는 보통 Collectors
클래스와 함께 사용하며, 다음과 같은 기능을 제공한다.
- Stream 요소들을 List, Set, Map 등으로 변환
- Stream 요소들을 결합
- Stream 요소들의 통계값 (최대값, 최소값, 평균값 등..)
- Stream 요소들의 그룹핑, 분할, ..
# Collectors.toList()
스트림을 List로 변환한다.
List<Integer> list = Stream.<Integer>of(2, 3, 1, 5, 6, 4)
.collect(Collectors.toList());
# Collectors.toSet()
스트림을 Set으로 변환한다.
Set<Integer> set = Stream.<Integer>of(2, 3, 1, 5, 6, 4)
.collect(Collectors.toSet());
set.forEach(System.out::println);
# Collectors.joining()
스트림을 하나의 문자열로 결합할 수 있다.
String str = Stream.<String>of("AAA", "BBB", "CCC", "DDD")
.collect(Collectors.joining());
System.out.println(str); // AAABBBCCCDDD
String str = Stream.<String>of("AAA", "BBB", "CCC", "DDD")
.collect(Collectors.joining("+", "<", ">"));
System.out.println(str); // <AAA+BBB+CCC+DDD>
# Collectors.count()
요소의 개수를 반환한다.
Long count = Stream.<Integer>of(2, 3, 1, 5, 6, 4)
.collect(Collectors.counting());