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을 알게 되었으면 아는 것에 멈추지 않고 자주 사용하며 응용하는 능력이 요구된다.
정리해놨으니 자주 사용하고 참고하면서 함수형 프로그래밍에 익숙해지자.