JAVA 8 에서는 함수형 인터페이스, 람다, 메소드 참조라는 개념이 추가되면서, 함수 객체를 더욱 쉽게 구현할수 있게 되었다.
이와 함께 스트림 API까지 추가 되어 데이터 원소의 시퀀스 처리를 라이브러리 차원에서 지원하기 시작하였다.
Collections.sort(words, comparingInt(String::length)); //변경전
word.sort(coparingInt(String:length)); //변경후
// 람다 사용
map.merge(key, 1, (count, incr) -> count + incr);
// 메서드 참조 사용
map.merge(key, 1, integer::sum);
class GoshThisClassNameIsHumongous {
// action 메서드 정의는 생략
public void withMethodReference() {
// 메서드 참조
servie.execute(GoshThisClassNameIsHumongous::action);
}
public void withLambda() {
// 람다
service.execute(() -> action());
}
}
메소드 참조 유형 | 예시 | 같은 기능을 하는 람다식 |
---|---|---|
정적 | Integer::parseInt | str -> Integer.parseInt(str) |
한정적(인스턴스) | Instant.now()::isAfter | Instant then = Instant.now(); t -> then.isAfter(t) |
비한정적(인스턴스) | String::toLowerCase | str -> str.toLowerCase() |
클래스 생성자 | TreeMap<K,V>::new | () -> new TreeMap<K,V>() |
배열 생성자 | int[]::new | len -> new Int[len] |
필요한 용도에 맞는게 있다면, 직접 구현하지 말고 표준 함수형 인터페이스를 활용하는것이 좋다.
기본 함수형 인터페이스에 박싱된 기본 타입을 넣어서 사용하지는 말자 (계산량이 많을때에는 성능이 확 떨어짐)
직접 만든 함수형 인터페이스에는 항상 @FuntionalInterface 어노테이션을 사용하자
-> JAVA 8 버전 부터는 람다를 지원하기 때문에 API를 설계할 때에 람다도 염두해야 한다.
-> 입력값과 반환값에 함수형 인터페이스 타입을 활용하자
-> 보통은 java.util.function 패키지의 표준 함수형 인터페이스를 사용하는것이 가장 좋은 선택이나 흔치 않치만 직접 새로운 함수형 인터페이스를 만들어 쓰는 편이 나을때도 있음을 잊지 말자.
java.util.function에는 43가지의 인터페이스가 있다. 여기선 대표적으로 6가지만 볼것이다. 나머지 함수는 대표 6개의 인터페이스만 기억하면 유추할수 있다.
메소드 참조 유형 | 함수 시그니처 | 의미 | 예시 |
---|---|---|---|
UnaryOperator<T> | T apply(T t) | 반환값과 인수의 타입이 같은 함수, 인수는 1개 | String::toLowerCase |
BinaryOperator<T> | T apply(T t1, T t2) | 반환값과 인수의 타입이 같은 함수, 인수는 2개 | BigInteger::add |
Predicate<T> | boolean test(T t) | 한 개의 인수를 받아서 boolean을 반환하는 함수 | Collection::isEmpty |
Function<T,R> | R apply(T t) | 인수와 반환 타입이 다른 함수 | Arrays::asList |
Supplier<T> | T get() | 인수를 받지 않고 값을 반환, 제공하는 함수 | Instant::now |
Consumer<T> | void accept(T t) | 한 개의 인수를 받고 반환값이 없는 함수 | System.out::println |
링크 : https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
스트림 API는 다량의 데이터 처리 작업 (순차, 병렬)을 돕고자 JAVA 8에서 추가되었다.
[게시글]JAVA 8 (3) Stream / Optional의 등장
데이터 소스가 Stream.iterate거나 중간 연산으로 limit을 사용하면 파이프라인 병렬화로는 성능 개선을 기대할수 없다.
public class ParallelMersennePrimes {
public static void main(String[] args) {
primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE))
.parallel() // 스트림 병렬화
.filter(mersenne -> mersenne.isProbablePrime(50))
.limit(20)
.forEach(System.out::println);
}
static Stream<BigInteger> primes() {
return Stream.iterate(TWO, BigInteger::nextProbablePrime);
}
}
대체로 스트림의 소스가 ArrayList, HashMap, HashSet, ConcurrentHashMap의 인스턴스거나 배열, int 범위, long 범위일때 병렬화의 효과가 가장 좋다.
스트림의 잘못 병렬화를 하면 (응답 불가를 포함해) 성능이 나빠질뿐 아니라 결과 자체가 잘못되거나 예측 못하는 동작이 발생할수 있다.
조건이 잘 갖춰지면 parlel 메소드 호출 하나로 거의 프로세서 코어 수에 비례하는 성능 향상을 만끽할수 있기 때문에 잘 써야 한다.