long counts = IntStream.of(1, 3, 5, 7, 9).count(); // 5
long sum = LongStream.of(1, 3, 5, 7, 9).sum(); // 25
OptionalInt min = IntStream.of(1, 3, 5, 7, 9).min();// 1
OptionalInt max = IntStream.of(1, 3, 5, 7, 9).max();// 9
DoubleStream.of(1.1, 2.2, 3.3, 4.4, 5.5)
.average()
.ifPresent(System.out::println); // 3.3
스트림이 비어있는 경우 count, sum은 0을 출력하게 됩니다.
하지만 평균,최소,최대 경우에는 표현할 수 없기 때문에 Optional을 이용해 리턴한다.
// Integer Example
List<Integer> ages = Arrays.asList(1,2,3,4,5,6,7,8,9);
int result = ages.stream().reduce(0, (subtotal, element) -> subtotal + element); // 45
int result2 = ages.stream().reduce(0, Integer::sum); // 45
// String Example
String[][] namesArray = new String[][] {
{"a", "b"}, {"c", "d"},
{"e", "f"}, {"g", "h"}
};
String reduceStrResult = Arrays.stream(namesArray)
.flatMap(innerArrays -> Arrays.stream(innerArrays))
.reduce("", (partString, element) -> partString+element); // abcdefgh
reduce는 누적된 값을 계산한다.
Integer Example
subtotal+element 결과가 subtotal이 되고, 그 다음 element랑 계속 더해가며 누적된다.
따라서 결과가 45로 누적되어 반환된다.
String Example
partString+element 결과가 partString이 되고, 그 다음 element랑 계속 합쳐지며 누적된다.
따라서 결과가 abcdefgh로 누적되어 합쳐져 반환된다.
스트림의 값을 모아주는 기능이다. 다시 컬렉션(Collections)으로 돌려준다.
String[] strs = new String[] {"aa", "bb","cc","bb", "dd", "eee"};
List<T> 형태로 결과를 반환한다.
List<String> result = Arrays.stream(strs).collect(Collectors.toList());
// "aa","bb","cc","bb","dd","eee"
Set<T> 형태로 결과를 반환한다.
Set<String> toSetResult = Arrays.stream(strs).collect(Collectors.toSet());
// "aa","bb","cc","dd","eee"
Map<T> 형태로 결과를 반환한다.
Map<String,Integer> toMapResult = Arrays.stream(strs)
.collect(Collectors.toMap(String::new, String::length));
// aa 2
// bb 2
// cc 2
// dd 2
// eee 3
또, toMap의 기능을 사용해서 List -> Map으로 변환할 수 있다.
class Book {
private String name;
private int releaseYear;
private String isbn;
// getters and setters
}
public Map<String, String> listToMap(List<Book> books) {
return books.stream()
.collect(Collectors.toMap(Book::getIsbn, Book::getName));
}
스트림의 작업 결과를 하나의 스트링(String)으로 연결한다. (Collection이 아닌 String으로)
세가지 인자를 입력할 수 있다.
delimiter: 각 요소 중간에 들어가는 구분자
prefix: 이어붙인 결과 맨 앞에 붙는 문자
suffix: 이어붙인 결과 맨 끝에 붙는 문자
String result = Arrays.stream(strs).collect(Collectors.joining(","));
// aa,bb,cc,bb,dd
String result = Arrays.stream(strs).collect(Collectors.joining(",","z",""));
// zaa,zbb,zcc,zbb,zdd
String result = Arrays.stream(strs).collect(Collectors.joining(",","","z"));
// aaz,bbz,ccz,bbz,ddz
collect 후, Collectors 결과를 리턴하는데 그 결과를 한번더 감싸서 어떠한 형태로 감싸서 다른 컬렉터를 반환한다.
Collections::unmodifiableList - List가 불변하도록 박제해버린 결과를 반환
Arrays.stream(strs).collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
Collection::toString - String 으로 반환
Arrays.stream(strs).collect(Collectors.collectingAndThen(Collectors.toList(), Collection::toString));
Optional::get - Optional get 이니까 리턴 값을 반환
Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Integer::intValue)), Optional::get)
특정 조건으로 요소들을 그룹화하여 Map 타입으로 반환한다.
예를 들어, 나이, 이름, 성별을 가진 클래스를 나이 기준으로 그룹화 할 수 있다.
예제에서는 String 길이에 따라 그룹핑하였다.
Map<Integer, List<String>> groupingByResult = Arrays.stream(strs)
.collect(Collectors.groupingBy(String::length, Collectors.toList()));
// 1. aa, bb, cc, dd
// 2. eee
Predicate로 특정 조건을 받아 해당 조건을 만족하면 true, 아니면 false 그룹으로 구분하여 Map 타입으로 반환한다.
Map<Boolean, List<String>> result = Arrays.stream(strs)
.collect(partitioningBy(s -> s.length() > 2))
// true: [eee], false: [aa,bb,cc,bb,dd]
요소(Integer)들의 평균을 Double형으로 반환한다.
double averagingInt = ages.stream().collect(Collectors.averagingInt(Integer::intValue));
요소들의 합을 Integer형으로 반환한다.
int summingInt = ages.stream().collect(Collectors.summingInt(Integer::intValue));
다양한 연산 결과를 IntSummaryStatistics 형으로 반환한다.
IntSummaryStatistics intSummaryStatistics = ages.stream().collect(Collectors.summarizingInt(Integer::intValue));
intSummaryStatistics.getSum(); // 총 합
intSummaryStatistics.getMin(); // 최소값
intSummaryStatistics.getMax(); // 최대값
intSummaryStatistics.getCount(); // 갯수
intSummaryStatistics.getAverage(); // 평균값
제공 메소드: getCount(), getSum(), getAverage(), getMin(), getMax()
filter처럼 조건을 작성하여 조건에 맞춰서 boolean을 리턴한다.
하나라도 조건을 만족하는 요소가 있는지 확인한다.
boolean isValidAnyMatch = Arrays.stream(strs).anyMatch(element -> element.contains("aa")); // true
모든 조건을 만족하는지 확인한다.
boolean isValidAllMatch = Arrays.stream(strs).allMatch(element -> element.contains("gggg")); // false
모든 조건을 만족하지 않는지 확인한다.
boolean isValidNoneMatch1 = Arrays.stream(strs).noneMatch(element -> element.contains("dd")); // false
boolean isValidNoneMatch2 = Arrays.stream(strs).noneMatch(element -> element.contains("zzzzz")); // true
요소를 순회하면서 실행되는 작업이다.
인자로 넘긴 메소드에 요소를 대입하여 호출한다.
주로 System.out::println 메소드 레퍼런스과 같은 출력 함수를 인자로 넘긴다.
Intermediate Operator에 속하는 peek와 유사하다고 볼 수 있다.
Arrays.asList("String","Integer","Boolean","Long").stream()
.forEach(System.out::println);
수학에서 사용하는 함수를 보다 단순하게 표현하는 방법이다.
람다 익스프레션을 Method Reference로도 대체가 가능하다.
람다는 다음과 같은 특징을 가진다.
Stream을 알게 되었으면 아는 것에 멈추지 않고 자주 사용하며 응용하는 능력이 요구된다.
정리해놨으니 자주 사용하고 참고하면서 함수형 프로그래밍에 익숙해지자.