Java 8 - 3. Stream
1. Stream
연속된 데이터를 처리하는 Operation들의 모음.
스트림은 데이터를 담고 있는 저장소가 아니며, 스트림으로 처리되는 데이터 소스는 변경되지 않는다.
오직 한 번만 처리하며 무제한일 수 있다.
중개 오퍼레이터와 종료 오퍼레이터로 나눌 수 있다.
중개 오퍼레이션
-
Stream을 리턴한다.
-
Stateless / Stateful 오퍼레이션으로 더 상세하게 구분할 수도 있다. (대부분은 Stateless지만 distinct나 sorted 처럼 이전 이전 소스 데이터를 참조해야 하는 오퍼레이션은 Stateful 오퍼레이션이다.)
-
filter, map, limit, skip, sorted, ...
종료 오퍼레이션
-
Stream을 리턴하지 않는다.
-
collect, allMatch, count, forEach, min, max, ...
2. Stream API
걸러내기
-
Filter(Predicate.not())
! 사용이 불가능 하기 때문에 Predicate.not()을 사용할 수 있다. -
예) 이름이 3글자 이상인 데이터만 새로운 스트림으로
package stream;
import java.util.Arrays;
import java.util.List;
public class practice {
public static void main(String[] args) {
List<String> names = Arrays.asList("유비", "관우", "제갈공명", "사마천", "사마의");
names.stream().filter(practice::nameLengthisThree)
.forEach(System.out::println);
}
private static boolean nameLengthisThree(String name) {
return name.length() == 3;
}
}
사마천
사마의
아래는 메서드 레퍼런스로 변경한 코드 결과는 동일하다.
package stream;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class practice {
public static void main(String[] args) {
List<String> names = Arrays.asList("유비", "관우", "제갈공명", "사마천", "사마의");
names.stream().filter(Predicate.not(practice::name))
.forEach(System.out::println);
}
private static boolean name(String name) {
return name.length() == 4;
}
}
- 예) 이름이 4글자가 아닌 데이터만 새로운 스트림으로
package stream;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class practice {
public static void main(String[] args) {
List<String> names = Arrays.asList("유비", "관우", "제갈공명", "사마천", "사마의");
names.stream().filter(Predicate.not(name->name.length()==4))
.forEach(System.out::println);
}
}
유비
관우
사마천
사마의
마찬가지로 아래는 위의 코드를 메서드 레퍼런스를 사용해 변경한 코드.
package stream;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class practice {
public static void main(String[] args) {
List<String> names = Arrays.asList("유비", "관우", "제갈공명", "사마천", "사마의");
names.stream().filter(Predicate.not(practice::nameLengthisNotFour))
.forEach(System.out::println);
}
private static boolean nameLengthisNotFour(String name) {
return name.length() == 4;
}
}
변경하기
-
Map(Function) 또는 FlatMap(Function)
package stream;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class practice {
public static void main(String[] args) {
List<Integer> scores = Arrays.asList(10,20,30,40,50,60,70,80,90,100);
Collection<String> stringScore = scores.stream()
.map(score -> score + "")
.collect(Collectors.toList());
System.out.println(stringScore);
}
}
map()을 이용해 String 타입으로 변환시켜 새로운 리스트에 저장한 예제이다.
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
flatmap()은 이중리스트를 스트림으로 보낼 때 각 리스트안의 요소들을 펼쳐 요소 하나하나가 스트림을 거쳐 지나갈 수 있게 만들어 주는 기능이다.
package stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class practice {
public static void main(String[] args) {
List<Integer> mathScore = Arrays.asList(10,20,30);
List<Integer> koreanScore = Arrays.asList(40,50,60);
List<Integer> engScore = Arrays.asList(70,80,90);
List<List<Integer>> subject = new ArrayList<>();
subject.add(mathScore);
subject.add(koreanScore);
subject.add(engScore);
subject.stream().flatMap(Collection::stream)
.forEach(System.out::println);
}
}
10
20
30
40
50
60
70
80
90
10 - 90까지 각 과목의 점수를 출력해주는 것을 확인할 수 있다. 이 메소드를 진작 알았더라면 우테코 3주차 미션에서 지하철 노선도 출력할 때 활용할 수 있었을텐데...
생성하기
-
generate(Supplier) 또는 Iterate(T seed, UnaryOperator)
-
예) 10부터 1씩 증가하는 무제한 숫자 스트림
package stream;
import java.util.stream.Stream;
public class practice {
public static void main(String[] args) {
Stream.iterate(10, i -> i+1)
.forEach(System.out::println);
}
}
10
11
12
...
-
예) 랜덤 int 무제한 스트림
package stream;
import java.util.Random;
import java.util.stream.Stream;
public class practice {
public static void main(String[] args) {
Random random = new Random();
Stream.iterate(10, i -> random.nextInt() )
.forEach(System.out::println);
}
}
10
-860808916
297488868
1860616718
...
제한하기
-
limit(long) 또는 skip(long)
그렇다면 생성 하는데 제한을 두려면 어떻게 해야 할까? -
예) 최대 5개의 요소가 담긴 스트림을 리턴한다.
package stream;
import java.util.stream.Stream;
public class practice {
public static void main(String[] args) {
Stream.iterate(10, i -> i+1)
.limit(5)
.forEach(System.out::println);
}
}
10
11
12
13
14
-
예) 앞에서 3개를 뺀 나머지 스트림을 리턴한다.
package stream;
import java.util.stream.Stream;
public class practice {
public static void main(String[] args) {
Stream.iterate(10, i -> i+1)
.limit(5)
.skip(3)
.forEach(System.out::println);
}
}
13
14
- 예) 10부터 1씩 증가하는 무제한 스트림 중 최대 10개까지만 증가하는 스트림
package stream;
import java.util.Random;
import java.util.stream.Stream;
public class practice {
public static void main(String[] args) {
Random random = new Random();
Stream.iterate(10, i -> i+1)
.limit(10)
.forEach(System.out::println);
}
}
10
11
12
...
19
- 예) 10부터 1씩 증가하는 무제한 스트림 중 앞의 5개는 제외후 10개까지 증가하는 스트림
package stream;
import java.util.Random;
import java.util.stream.Stream;
public class practice {
public static void main(String[] args) {
Random random = new Random();
Stream.iterate(10, i -> i+1)
.skip(5)
.limit(10)
.forEach(System.out::println);
}
}
15
16
17
...
24
// 10, 11, 12, 13, 14
// 5개는 skip하고 나머지의 것들을 10개 보여준다.
스트림에 있는 데이터가 특정 조건을 만족하는지 확인
-
anyMatch(), allMatch(), nonMatch()
이 스트림 메소드들은 우테코 과제를 진행하며 자연스럽게 자주 익히게 되었다. 하나라도 일치하는 경우, 전부 일치해야하는 경우, 하나도 일치하지 않는 경우를 의미힌다.
-
예) k로 시작하는 문자열이 있는지 확인한다. (true 또는 false를 리턴한다.)
package stream;
import java.util.Arrays;
import java.util.List;
public class practice {
public static void main(String[] args) {
List<String> names = Arrays.asList("kim", "lee", "park", "kang");
boolean result = names.stream().anyMatch(name -> name.startsWith("k"));
System.out.println(result);
}
}
true
-
예) 스트림에 있는 모든 값이 10보다 작은지 확인한다.
package stream;
import java.util.Arrays;
import java.util.List;
public class practice {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
boolean result = numbers.stream().allMatch(n -> n<10);
System.out.println(result);
}
}
false
- 예) 스트림의 값 중 100이 있는지 확인한다.
package stream;
import java.util.Arrays;
import java.util.List;
public class practice {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
boolean result = numbers.stream().noneMatch(n -> n==100);
System.out.println(result);
}
}
true
개수 세기
-
count()
-
예) 10보다 큰 수의 개수를 센다.
package stream;
import java.util.Arrays;
import java.util.List;
public class practice {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
long count = numbers.stream()
.filter(number -> number>10)
.count();
System.out.println(count);
}
}
2
반환 타입이 long인 것을 주의하자!!
스트림을 데이터 하나로 뭉치기
-
reduce(identity, BiFunction), collect(), sum(), max()
-
예) 모든 숫자 합 구하기
package stream;
import java.util.Arrays;
import java.util.List;
public class practice {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
System.out.println(numbers.stream().mapToInt(i->i).sum());
}
}
54
-
예) 모든 데이터를 하나의 List 또는 Set에 옮겨 담기
package stream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class practice {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
List<Integer> numList = numbers.stream().collect(Collectors.toList());
System.out.println(numList);
}
}
[1, 3, 5, 10, 15, 20]
package stream;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class practice {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1,3,5,10,15,20);
Set numList = numbers.stream().collect(Collectors.toSet());
System.out.println(numList);
}
}
[1, 3, 20, 5, 10, 15]