스트림 API는 다량의 데이터 처리 작업(순차적이든 병렬적이든)을 돕고자 자바 8에 추가됨.
소스 스트림에서 시작해 종단 연산으로 끝나며, 그 사이에 하나 이상의 중간 연산이 있을 수 있다.
소스 스트림 -> 1개 이상의 중간 연산 -> 종단 연산
각 중간 연산은 스트림을 어떠한 방식으로 변환한다. 예를 들면, 각 원소에 함수를 적용하거나 특정 조건을 걸어 필터링할 수 있는 식이다.
map 입력 T 타입 요소를 R 타입 요소로 변환
filter 조건을 충족하는 요소를 필터링
flatMap 중첩된 구조를 한 단계 평탄화하고 단일 원소로 변환한 스트림 생성
peek 스트림 내의 각각의 요소를 대상으로 특정 연산을 수행
skip 처음 n개의 요소를 제외하는 스트림 생성
limit maxSize까지의 요소만 제공하는 스트림 생성
distinct 스트림 내의 요소의 중복 제거
sorted 스트림 내 요소를 정렬
종단 연산은 중간 연산이 내놓은 스트림에 최후 연산을 가한다. 원소를 정렬해 컬렉션에 담거나 모든 원소를 출력하는 식으로 사용할 수 있다.
forEach 스트림을 순회
reduce 연산을 이용해 모든 스트림 요소를 처리하여 하나의 결과로 만듦
collect 스트림의 연산 결과를 컬렉션 형태로 모아줌
그때 그때 값을 평가하지 않고, 정말 결과값이 필요한 시점까지 평가를 미루는 것으로 무한 스트림을 다룰 수 있게 해준다.
지연 평가를 하지 않는다면 중간 연산이 끝나지 않는다.
스트림 API = 플루언트 API
메서드 연쇄를 지원
파이프라인 하나를 구성하는 모든 호출을 연결해 하나의 표현식을 만든다.
기본적으로 스트림 파이프라인은 순차적으로 진행이 된다.
병렬로 실행하기 위해서는 parallel() 을 호출하면 된다. (그러나 효과는 미미)
스트림을 과용하면 프로그램이 읽기 어려워지고 유지보수하기 힘들어지는 경우가 있다.
public static void main(String[] args) {
File dectionary = new File(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
Map<String, Set<String>> groups = new HashMap<>();
try (Scanner s = new Scanner(dectionary)) {
while(s.hasNext()) {
String word = s.next();
groups.computeIfAbsent(alphabetize(word),
(unused) -> new TreeSet<>()).add(word);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
for(Set<String> group : groups.values()) {
if(group.size() >= minGroupSize) {
System.out.println(group.size() + ": " + group);
}
}
}
private static String alphabetize(String s) {
char[] a = s.toCharArray();
Arrays.sort(a);
return new String(a);
}
public static void main(String[] args) throws IOException {
File dectionary = new File(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
try(Stream<String> words = Files.lines(dectionary.toPath())) {
words.collect(
groupingBy(word -> word.chars().sorted()
.collect(StringBuilder::new,
(sb, c) -> sb.append((char) c),
StringBuilder::append).toString()))
.values().stream()
.filter(group -> group.size() >= minGroupSize)
.map(group -> group.size() + ": " + group)
.forEach(System.out::println);
}
}
public static void main(String[] args) throws IOException {
File dectionary = new File(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
try(Stream<String> words = Files.lines(dectionary.toPath())) {
words.collect(groupingBy(word -> alphabetize(word)))
.values().stream()
.filter(group -> group.size() >= minGroupSize)
.forEach(group -> System.out.println(group.size() + ": " + group));
}
}
static Stream<BigInteger> primes() {
return Stream.iterator(TWO, BigInteger::nextProbablePrime);
}
public static void main(String[] args) {
primes().map(p -> TWO.pow(p.intValueExact().subtract(ONE)))
.filter(mersenne -> mersenne.isProbablePrime(50))
.limit(20)
.forEach(System.out::println);
}
private static List<Card> newDeck() {
List<Card> result = new ArrayList<>();
for(Suit suit : Suit.values())
for(Rank rank : Rank.values())
result.add(new Card(suit, rank));
return result;
}
private static List<Card> newDeck() {
return Stream.of(Suit.values())
.flatMap(suit -> Stream.of(Rank.values())
.map(rank -> new Card(suit, rank)))
.collect(toList());
}
<<핵심 정리>>
스트림과 반복문, 어느 것을 사용해야 할 지 고민된다면 둘 다 해보고 결정하자.
Why are char[] the only arrays not supported by Arrays.stream()?
Why is String.chars() a stream of ints in Java 8?