728x90

collect() 

collect()는 스트림의 요소를 수집하는 최종연산이다.

collect에는 어떻게 수집할 것인가에 대한 방법이 필요하는데 이러한 방법을 정의한것이 컬렉터(collector)이다.

 

컬렉터는 Collector 인터페이스를 구현한 것으로, 직접 구현하거나 미리 작성된 것을 사용할 수 있다. 

Collectors클래스는 미리 작성된 다양한 컬렉터를 반환하는 static메서드를 가지고 있다.

 

스트림의 요소를 수집하는 collect()의 매개변수로는 수집하는 방법인 컬렉터가 사용된다.

해당 컬렉터는 Collector 인터페이스를 통해 구현하거나, Collectors클래스의 static메서드를 통해 얻을 수 있다.

 

collect() : 스트림의 최종연산, 매개변수로 컬렉터가 필요하다.

Collector : 인터페이스로 컬렉터를 구현할 때 사용한다.

Collectors : 클래스이다. 미리작성된 static메서드로 컬렉터를 제공한다.

 

collect() 선언

Object collect(Collector collector) //Collector인터페이스를 구현한 객체를 매개변수로 사용한다.

Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)

 

스트림을 컬렉션과 배열로 변환 - toList(), toSet(), toMap(), toCollection(), toArray()

스트림의 모든 요소들을  컬렉션에 수집하기 위한 메서드들이다.

List<String> names = stuStream.map(Student::getName).collect(Collectors.toList());

Student스트림의 모든 요소들의 이름을 List로 수집한 코드이다.

 

ArrayList<String> list = names.stream().collect(Collectors.toCollection(ArrayList::new));

List<String>을 ArrayList로 할당한 코드이다.

List나 Set은 toList(), toSet()이 있지만 다른 컬렉션을 사용하고 싶은 경우 Collectors의 toCollection()에 해당 컬렉션 생성자를 넣어주면 가능하다.

 

toMap()

Map<String, Person> map = personStream.collect(Collectors.toMap(p->p.getRegId(), p->p));

toMap의 경우 key와 value를 각각 지정해 주어야한다.

 

스트림에 지정된 요소들을 배열로 저장하는 메서드 toArray()

toArray()에 매개변수로 해당 타입의 생성자 참조를 넣어주어야한다. 매개변수를 작성하지 않을시 반환되는 배열의 타입은 Object[] 이다.

Student[] stuNames = studentStream.toArray(Student[]::new);

Object[] stuNames = studentStream.toArray();

 

통계 - counting(), summingInt(), averagingInt(), maxBy(), minBy()

개수, 합, 평균, 최대값, 최솟값등을 반환하는 메서드이다.

 

ex)

long count = stuStream.count();

long count = stuStream.collect(Collectors.counting());

 

long totalScore = stuStream.mapToInt(Student::getTotalScore).sum();

long totalScore = stuStream.collect(summingInt(Student::getTotalScore));

 

OptionalInt topScore = studentStream.mapToInt(Student::getTotalScore).max();

Optional<Student> topStudent = stuStream.max(Comparator.comparingInt(Student::getTotalScore));

Optional<Student> topStudent = stuStream.collect(maxBy(Comparator.comparingInt(Student::getTotalScore));

 

IntSummaryStatics stat = stuStream.mapToInt(Student::getTotalScore).summaryStatistics();

IntSummaryStatics stat = stuStream.collect(summarizingInt(Student::getTotalScore));

 

위 예시는 collect를 사용한경우와 사용하지않은 경우를 비교한 것이다. collect사용법으로 알아두면 좋을것 같다.

 

리듀싱 - reducing()

리듀싱 선언

Collector reducing(BinaryOperator<T> op)

Collector reducing(T identity, BinaryOperator<T> op)

Collector reducing(U identity, Function<T, U> mapper, BinaryOperator<U> op)

 

사용예시

IntStream intStream = new Random().ints(1,46).distinct().limit(6);

 

OptionalInt max = intStream.reduce(Integer::max);

Optional<Integer> max = intStream.boxed().collect(reducing(Intger::max));

//intStream을 boxed()를 통해 Stream<Integer>로 변경한다.그래야 Stream에 선언된 collect를 사용할 수 있다.

 

long sum = intStream.reduce(0, (a,b) -> a+b);

long sum = intStream.boxed().collect(reducing(0,(a,b)->a+b));

 

int grandTotal = stuStream.map(Student::getTotalScore).reduce(0, Integer::sum);

int grandTotal = stuStream.collect(reducing(0, Student::getTotalScore, Integer::sum));

 

문자열 결합 - joining()

스트림의 모든요소를 하나의 문자열로 결합하여 반환한다. 구분자 지정및 접두사, 접미사 지정도 가능하다.

스트림의 요소가 문자열이 아닌경우에는 map()을 통해 문자열로 변환해주어야한다.

 

ex))

String studentNames = stuStream.map(Student::getName).collect(joining());

String studentNames = stuStream.map(Student::getName).collect(joining(","));

String studentNames = stuStream.map(Studednt::getName).collect(joining(",","[","]");

 

728x90

'Programming > JAVA' 카테고리의 다른 글

자바의 입출력  (0) 2021.09.10
그룹화와 분할 - groupingBy(), partitioningBy()  (0) 2021.09.02
스트림의 최종연산  (0) 2021.09.01
Optional  (0) 2021.08.25
스트림(Stream)의 중간연산  (0) 2021.08.25
728x90

스트림의 최종연산

스트림을 다루는데 중간연산과 최종연산이 사용된다.

최종 연산의 경우 스트림의 요소를 소모해서 결과를 만들어내기 때문에 최종연산 이후 스트림은 없어진다.

 

forEach()

void forEach(Consumer<? super T> action)

반환타입이 void이며 스트림의 요소들을 출력하는 용도로 많이 사용되는 최종연산이다.

 

allMatch(), anyMatch(), noneMatch(), findFirst(), findAny() - 조건검사

지정한 조건에 일치하는 스트림 요소가 있는지, 조건에 모두 일치하는지, 조건에 모두 일치하지 않는지 등을 확인하는데 사용되는 최종연산이다.

 

boolean allMatch(Predicate <? super T> predicate) : 조건식에 모든 요소가 일치하면 true반환

boolean anyMatch(Predicate <? super T> predicate) : 어떤 요소하나라도 조건에 일치하면 true반환

boolean noneMatch(Predicate<? super T> predicate) : 조건식에 모든 요소들이 일치하지 않는다면 true반환

 

filter()연산과 함께 사용되는 findFirst(), findAny()가 있다.

filter()를 적용하여 조건식에 맞는 요소가 있는지 확인할 때 사용한다.

findAny와 findFirst()는 반환 타입이 Optional<T>이며, 스트림의 요소가 없을 때는 비어있는 Optional 객체를 반환한다.

 

count(), sum(), average(), max(), min() - 통계

스트림의 요소들의 통계에 대한 정보를 얻을 수 있는 연산이다. 요소의 개수, 요소들의 합, 평균, 가장큰값, 가장 작은 값 등을 얻을 수 있다.

long count()

Optional<T> max(Comparator<? super T> comparator)

Optional<T> min(Comparator<? super T> comparator)

 

리듀싱 - reduce()

스트림의 요소를 하나씩 줄여나가면서 연산을 수행하고 모든 요소들을 소모하여 나온 결과를 반환한다.

Optional<T> reduce(BinaryOperator<T> accumulator)

두가지 요소를 연산하여 결과를 반환한다.

 

T reduce(T identity, BinaryOperator<T> accumulator)

-> 초기값(identity)를 가지고 연산(accumulator)를 수행한다.

U reduce(U identity, accumulator, BinaryOperator<U> combiner)

->combiner는 병렬 스트림에 의해 처리된 결과를 합할 때 사용한다.

 

reduce 사용예시

int count = intStream.reduce(0, (a,b)-> a+1);

int sum = intStream.reduce(0,(a,b)->a+b);

int max = intStream.reduce(Integer.MIN_VALUE, (a,b) -> a>b ? a: b);

int min = intStream.reduce(Integer.MAX_VALUE, (a,b) -> a < b? a:b);

 

a에 초기값이 대입되고 스트림요소들이 차례대로 b에 대입된다. 이후 연산결과가 a에 대입되며 b에 새로운요소가 들어오게된다.

최종연산 count()와 sum()의 경우 내부적으로 reduce()를 사용한다.

 

reduce의 반환타입은 Optional이다. IntStream의 reduce의 경우 반환 타입이 OptionalInt이다. 

OptionalInt에 저장된 값을 꺼낼때는 getAsInt()메서드를 사용하면된다.

ex)

OptionalInt max = intStream.reduce(Integer::max);

int maxValue = max.getAsInt();

 

 

728x90

'Programming > JAVA' 카테고리의 다른 글

그룹화와 분할 - groupingBy(), partitioningBy()  (0) 2021.09.02
collect()  (0) 2021.09.02
Optional  (0) 2021.08.25
스트림(Stream)의 중간연산  (0) 2021.08.25
스트림(Stream)  (0) 2021.08.24
728x90

Stream의 최종연산 결과가 종종 Optional타입인 경우가 있다.

Optional은 Stream의 최종연산결과를 다룰때 사용된다.

Optional<T>는 T타입의 객체 를 감싸는 래퍼 클래스이다. Optional타입의 객체에는 모든 타입의 참조변수를 담을 수 있다.

 

public final class Optional<T>

{

     private final T value;

      ...

}

 

Optional타입의 객체로 최종연산의 결과를 반환하는경우 Optional객체에 결과를 담아서 반환하는 것이다.

Optional객체의 경우 null을 결과로 받더라도 NullPointerException을 발생시키지 않아 유용하다.

 

Optional객체 생성

Optional객체를 생성할 때는 of() 또는 ofNullalble()을 사용한다.

ex)

String str = "abc";

Optional<String> optVal = Optional.of(str);

Optional<String> optVal = Optional.of("abc");

Optional<String> optVal = Optional.of(new String("abc"));

 

참조변수가 null일 가능성이있다면 of()대신 ofNullable()을 사용해야한다.

of()의경우 NullPointerException을 발생시키기 때문이다.

 

Optional객체 값 가져오기

get() : Optional 객체에 저장된 값을 가져올 때 사용하는 메서드이다.

orElse() : 값이 null일 경우 NoSuchElementException이 발생하는데 이것을 방지하기 위한 메서드

T orElseGet(Supplier<? extends T> other) : null을 대체할 값을 반환하는 람다식을 지정할 수 있다.

T orElseThrow(Supplier<? extends X> exceptionSupplier) : null일 때 지정된 예외를 발생시킬 수 있다.

 

Optional객체도 filter(), map(), flatMap()을 사용할 수 있다. 단 Optional의 map, flatMap의 반환 타입은 Stream이 아닌 Optional이다. 

 

isPresent()는 Optional객체의 값이 null이면 false를, 아니면 true를 반환한다.

 

Optional<T>를 반환하는 메서드 목록

Optional<T> findAny()

Optional<T> findFirst()

Optional<T> max(Comparator<? super T> comparator)

Optional<T> min(Comparator<? super T> comparator)

Optional<T> reduce(BinaryOperator<T> accumulator)

 

Optional 기본형

OptionalInt, OptionalLong, OptionalDouble로 각 기본형을 다루는 Optional들이 있다.

 

IntStream 에 정의된 메서드 목록

OptionalInt findAny()

OptionalInt findFirst()

OptionalInt reduce(IntBinaryOperator op)

OptionalInt max()

OptionalInt min()

OptionalDouble average()

 

 

Optional을 다루는 예제이다.

728x90

'Programming > JAVA' 카테고리의 다른 글

collect()  (0) 2021.09.02
스트림의 최종연산  (0) 2021.09.01
스트림(Stream)의 중간연산  (0) 2021.08.25
스트림(Stream)  (0) 2021.08.24
다양한 함수형 인터페이스 & 메서드 참조  (0) 2021.08.22
728x90

스트림의 중간연산

스트림 자르기 - skip(), limit()

skip()메서드와 limit()메서드는 스트림을 자르는데 사용한다.

Stream<T> skip(long n) : 스트림의 요소 처음부터 n개만큼 삭제한다.

Stream<T> limit(long maxSize) : 스트림의 요소중 maxSize만 남기고 제거한다.

 

스트림의 요소 걸러내기 - filter(), distinct()

Stream<T> filter(Predicate<? super T> predicate) : 주어진 조건(Predicate)에 해당하지않는 요소들을 제거한다.

Stream<T> distinct() : 중복되는 요소들을 제거하여 하나만 남긴다.

 

정렬 - sorted()

스트림을 정렬할 때 사용하는 sorted()메서드 이다.

Stream<T> sorted()

Stream<T> sorted(Comparator<? super T> comparator)

 

sorted()메서드는 매개변수로 주어지는 Comparator로 스트림을 정렬한다. Comparator를 지정하지않으면 스트림 요소의 기본 정렬 기준으로 정렬된다.

 

주어진 Student스트림을 정렬하는 예제이다. Ban을 기준으로 정렬하고 naturalOrder()를 통해 클래스에 정의된 compareTo 기준(totalScore)으로 정렬한다. 이후 각각 값들을 하나씩 출력한다.

변환 - map()

map() : 스트림에 저장된 값중 원하는 필드만 뽑아내거나 특정 형태로 변환해야 할 때 사용하는 메소드이다.

Stream<R> map(Function<? super T, ? extends R> mapper)

map()은 중간연산이기 때문에 filenameStream을 보면 최종연산으로 forEach를 사용한다.

fileStream을 보면 map(File::getName)을 통해 Stream<String>으로 변경하고, filter를 통해 확장자가 있는지 확인한다.

이후 확장자만 잘라내기위해 .이후의 값들을 확인. ->모든 확장자 글자들을 대문자로 변경 -> 중복제거 -> 출력 순으로 진행한다.

조회 - peek()

중간연산으로 연산중 결과를 확인할 수 있는 메서드이다.

연산사이에 삽입하여 연산의 결과가 올바르게 나왔는지 확인 가능하다.

중간연산 사이에 filename과 extension을 출력하여 결과를 확인한다.

Stream의 연산의경우 최종연산이 호출되면 중간연산들을 수행하기때문에 하나씩 출력된다.

 

mapToInt(), mapToLong(), mapToDouble()

map()은 연산의 결과로 Stream<T> 타입의 스트림을 반환하는데, 스트림의 요소를 숫자로 변환하는 경우 IntStream과 같은 기본형 스트림으로 변환하는것이 더 유용하다.

기본형 스트림으로 변환하기 위한 메서드들이다.

IntStream mapToInt(ToIntFunction<? super T> mapper);

DoubleStream mapToDouble(ToDoubleFunction <? super T> mapper);

LongStream mapToLong(ToLongFunction<? super T> mapper)

 

IntStream과 같은 기본형 스트림의 메서드

int sum() : 스트림의 모든요소의 합

OptionalDouble average() : sum() / (double)count()

OptionalInt max() : 스트림 요소중 최대값

OptionalInt min() : 스트림 요소중 최소값

 

sum()이나 average()같은 메소드들은 최종연산이기 때문에 한번 호출할경우 스트림이 닫힌다.

연속적으로 해당 메서드들을 호출해야할 경우 summaryStatistics()메서드를 통해서 IntSummaryStatistics형 자료를 따로 생성해주는것이 좋다.

IntStream을 통해 SummaryStatistics()를 호출할 경우 위처럼 getSum(), getAverage()등을 연속적으로 호출이 가능하다.

flatMap() - Stream<T[]>를 Stream<T>로 변환

스트림의 요소가 배열이거나 map()의 연산결과가 배열인 경우 스트림의 타입이 Stream<T[ ]>으로 생성된다.

이러한 경우 Stream<T>로 다루는것이 더 편한데, 이러한 경우 map()이 아닌 flatMap()을 사용해주면 된다.

flatMap을 통해 하나의 스트림으로 정리된것을 알 수 있다.

728x90

'Programming > JAVA' 카테고리의 다른 글

스트림의 최종연산  (0) 2021.09.01
Optional  (0) 2021.08.25
스트림(Stream)  (0) 2021.08.24
다양한 함수형 인터페이스 & 메서드 참조  (0) 2021.08.22
람다식(Lambda expression)  (0) 2021.08.21
728x90

스트림

스트림 : 다양한 데이터소스를 표준화된 방법으로 다루기 위한 것.

스트림은 데이터를 추상화하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의해 놓았다.

 

스트림은 스트림 생성 -> 중간연산(0~n번) ->최종연산(1번) 으로 사용된다.

스트림의 특징

1.스트림은 데이터 소스를 변경하지 않는다.

스트림은 데이터를 읽기만 할 뿐, 데이터 소스를 변경하지 않는다. 데이터를 정렬하거나 중복제거 등은 가능하지만 데이터 값을 변경하는것은 불가능하다.(Read only)

 

2.스트림은 1회용이다.

스트림의 경우 최종연산을 하게되면 해당 스트림이 닫힌다. 닫힌 스트림은 사용할 수 없으며 사용시 에러가 발생한다.

 

3. 스트림의 작업은 내부 반복으로 처리한다.

내부 반복 : 반복문을 메서드의 내부에 숨길 수 있다.

forEach()의 경우 스트림에 정의된 메서드로 매개변수에 대입된 람다식을 데이터 소스의 모든 요소에 적용시켜주는 메서드이다.

 

4.스트림의 연산

1.중간연산 : 연산 결과가 스트림을 반환하는 연산. 스트림에 연속적으로 연산가능

2.최종연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하기 때문에 스트림당 한번만 수행이 가능

최종연산 이후에는 스트림이 닫히기 때문에 최종연산은 한번만 가능하다.

 

연산목록

중간연산

Stream<T> distinct() : 중복 제거

Stream<T> filter(Predicate<T> predicate) : 조건에 안맞는 요소 제외

Stream<T> limit(long maxSize) : 스트림의 개수 지정

Stream<T> skip(long n) : 스트림의 일부를 건너 뛴다.

Stream<T> peek(Consumer<T> action) : 스트림의 요소에 작업 수행

Stream<T> sorted()

Stream<T> sorted(Comparator<T> comparator)

:스트림의 요소 정렬

 

Stream<R> map(Function<T,R> mapper) : 스트림의 요소 변환

 

최종연산

void forEach(Consumer<? super T> action) : 각 요소에 지정된 작업 수행

long count() : 스트림 요소 개수 반환

Optional<T> max(Comparator<? super T> comparator) : 스트림의 최대값 반환

Optional<T> min(Comparator<? super T> comparator) : 스트림의 최소값을 반환

Optional<T> findAny(), Optional<T> findFirst() : 스트림의 요소 하나를 반환

boolean allMatch(Predicate<T> p) : 모든 요소가 조건을 만족하는지 확인

boolean anyMatch(Predicate<T> p) : 하나의 요소라도 해당 조건을 만족하는지 확인

boolean noneMatch(Predicate<> p) : 모든 요소가 조건을 만족하지 않으면 true

Object[] toArray(), A[] toArray(IntFunction<A[ ]> generator) : 스트림의 모든 요소를 배열로 변환

Optional<T> reduce(BinaryOperator<T> accumulator)

T reduce(T identity, BinaryOperator<T> accumulator)

U reduce(U identity, BiFunction<U, T, U> accumulator, BinaryOperator<U> combiner)

 : 스트림의 요소를 하나씩 줄여 가면서 계산한다.

R collect(Collector<T,A,R> collector)

R collect(Supplier<R> supplier, BiConsumer<R, T> accumulator, BiConsumer<R, R> combiner)

: 스트림의 요소를 수집한다. 주로 요소를 그룹화 하거나 분할한 결과를 컬렉션에 담아 반환하는데 사용된다.

 

스트림 연산에서 중요한점은 최종연산이 수행되기 전까지 중간 연산은 수행되지않는다.

중간연산을 호출하더라도 즉각적인 연산이 수행되는것이 아니다.

최종 연산이 수행되어야 스트림의 요소들이 중간연산을 거쳐 최종 연산에서 소모된다.

 

5.기본형 스트림

스트림은 기본적으로 Stream<T> 형태이지만, 오토박싱&언박싱으로 인한 비효율을 줄이기 위해 데이터 소스의 요소를 기본형으로 다루는 스트림, IntStream, LongStream, DoubleStream이 제공된다. 

일반적으로 Stream<Integer> 보다 IntStream을 사용하는것이 더 성능이 좋다. 또한 IntStream과 같이 기본형이 적용된 스트림의 경우 기본형에 대한 작업에 유용한 메서드들이 더 존재한다.

 

6.병렬 스트림

스트림으로 데이터를 처리할 때 장점이 병렬처리가 쉽다는 것이다. 

병렬스트림으로 전환하는 경우 parallel()이라는 메서드를 사용하는데 해당 메서드를 호출하면 스트림이 병렬 스트림으로 전환된다. 병렬로 처리되지 않게 하려면 sequential()을 호출하면 된다.

 

 

스트림 만들기

스트림의 소스가 될 수 있는 대상은 배열, 컬렉션, 임의의 수 등이 있다. 이러한 소스들을 이용하여 스트림을 만들 수 있다.

 

컬렉션

Collection에 stream()메서드라 정의되어 있어 해당 메소드를 호출하면 스트림을 생성할 수 있다.

stream()메서드는 해당 컬렉션을 소스로 하는 스트림을 반환한다.

Stream<T> Collection.stream()

ex)

List<Integer> list = Arrays.asList(1,2,3,4,5);

Stream<Integer> intStream = list.stream();

 

배열

배열을 소스로하는 스트림을 생성할 때는 Stream과 Arrays에 정의되어있는 static메서드로 생성할 수 있다.

Stream<T> Stream.of(T ... values)

Stream<T> Stream.of(T[])

Stream<T> Arrays.stream(T[])

Stream<T> Arrays.stream(T[ ] array, int startInclusive, int endExclusive)

 

특정 범위의 정수

IntStream과 LongStream은 지정한 범위의 연속된 정수를 스트림으로 생성해서 반환할 수 있다.

IntStream IntStream.range(int begin, int end)

IntStream IntStream.rangeClosed(int begin, int end)

range()는 begin ~ end-1까지의 수를 스트림으로 생성하고 rangeClosed()는 begin ~ end 까지의 수를 스트림으로 생성한다.

 

forEach()는 매개변수로 들어온 람다식의 작업을 스트림의 모든요소에 대해 수행한다.

intStream.forEach(System.out::println);

foroEach()가 스트림의 요소를 소모하면서 작업을 수행하므로 같은 스트림에 forEach()를 두 번 호출할 수 없다.

스트림의 요소를 한번 더 출력하려면 스트림을 새로 생성해야 한다.

 

임의의 수

난수를 생성하는데 사용하는 Random클래스에 난수들로 이루어진 스트림을 반환하는 메서드가 있다.

IntStream ints()

LongStream longs()

DoubleStream doubles()

 

위 메서드들이 반환하는 스트림은 크기가 정해지지 않은 '무한 스트림(infinite stream)' 이다.

스트림의 개수를 지정해주는 limit()을 함께 사용하여 무한스트림을 유한 스트림으로 만들어 주어야한다.

ex)

IntStream intStream = new Random().ints();

intStream.limit(5).forEach(System.out::println);

위처럼 무한스트림을 유한스트림으로 변경하여 5개의 정수형 난수를 출력 할수 있다.

 

IntStream ints(long streamSize)

LongStream longs(long streamSize)

DoubleStream doubles(long streamSize)

 

위 메서드들은 지정된 크기에 해당하는 '유한 스트림'을 생성하여 반환한다.

 

람다식 - iterate(), generate()

iterate()와 generate()는 람다식을 매개변수로 받아 람다식에 해당하는 결과를 요소로하는 무한 스트림을 생성하여 반환한다.

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

: seed값부터 시작해서 람다식 f에 의해 계산된결과를 스트림에 요소에 포함시키며 해당 결과를 다시 seed값으로 넣어 계산을 반복한다.

static <T> Stream<T> generate(Supplier<T> s)

: seed값 없이 s의 결과로만 무한 스트림을 생성한다.

 

728x90

'Programming > JAVA' 카테고리의 다른 글

Optional  (0) 2021.08.25
스트림(Stream)의 중간연산  (0) 2021.08.25
다양한 함수형 인터페이스 & 메서드 참조  (0) 2021.08.22
람다식(Lambda expression)  (0) 2021.08.21
fork & join  (0) 2021.08.20
728x90

다양한 함수형 인터페이스

java.util.function 패키지에 일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해 두었다. 일반적으로 많이쓰이는 이패키지의 인터페이스를 활용하면 재사용성이나 유지보수 측면에서도 좋을것이다.

 

목록

Supplier<T> : 메서드 T get() - 매개변수는 없고, 반환값만 있다. (공급자)

Consumer<T> : 메서드 void accept(T t) - Supplier와 반대로 매개변수만 있고, 반환값이 없다. (소비자)

Function<T, R> : 메서드 R apply(T t) - 일반적인 하나의 매개변수와 반환값이 있는 함수. 입력타입 T 반환타입 R (입출력 함수)

Predicate<T> : 메서드 boolean test(T t) - 조건식을 표현하는데 사용됨. 매개변수 하나, 반환타입은 boolean(조건식)

 

매개변수가 두개인 함수형 인터페이스

매개변수가 두개인 함수형 인터페이스의 이름에는 'Bi'가 붙는다.

 

BiConsumer<T, U> : 메서드 void accept(T t, U u) - 두개의 매개변수만 있고 반환값은 없는 소비자

BiPredicate<T, U> : 메서드 boolean test(T t, U u) - 조건식을 표현하고 수행하는데 사용된다. 매개변수는 두개이고 반환값은 boolean이다.

BiFunction<T,U,R> : 메서드 R apply(T t, U u) - 두개의 매개변수 t, u를 받아서 결과를 반환한다. 반환 타입은 R

 

3개이상의 매개변수를 사용하는 함수형 인터페이스를 사용하고싶다면 직접 만들어서 사용해야한다.

ex)

@FunctionalInterface

interface TriFunction<T, U, V, R>{

     R apply(T t, U u, V v);

}

 

켤렉션 프레임웍과 함수형 인터페이스

컬렉션 프레임워크의 인터페이스에 함수형인터페이스가 존재한다.

 

UnaryOperator<T> : T apply(T t) - Function의 자손, Function과 달리 매개변수와 결과의 타입이 같다.

BinaryOperator<T> : T apply(T t, T t) - BiFunction의 자손, BiFunction과 달리 매개변수와 결과의 타입이 같다.

 

메서드 목록

Collection

boolean removeIf(Predicate<E> filter) : Predicate 조건식에 일치하는 값들을 삭제

List

void replaceAll(UnaryOperator<E> operator)  : 모든 요소 UnaryOperator의 함수의 반환값으로 변환하여 대체

Iterator 

void forEach(Consumer<T> action) : 모든 요소에 작업 action을 수행

Map

V comput(K key, BiFunction<K,V> f) : 지정된 키의 값에 작업 f를 수행

V computeIfAbsent(K key, Function<K,V> f) : 키가 없으면, 작업 f를 수행 후 추가

V computeIfPresent(K key, BiFunction<K,V,V> f ) : key가 있을때, 작업 f를 수행

V merge(K key, V value, BiFunction<V,V,V> f) : 모든 요소에 병합작업 f를 수행

void forEach(BiConsumer<K, V> action) : 모든 요소에 작업 action을 수행

void replaceAll(BiFunction<K,V,V> f) : 모든요소에 치환작업 f를 수행

 

list.forEach를 사용하고 매개변수로 람다식을 주었다. list의 모든 요소를 출력한다.

2번째줄 출력은 list.removeIf()를 수행한 이후이다. x가 2의배수거나 3의배수라면 모두 삭제하는 연산이다.

3번째줄 출력은 replaceAll을 통해 모든 요소들을 10배하여 요소값을 바꾸는 것이다.

4번째줄은 forEach를 통해 맵의 모든 요소들을 출력한 것이다.

 

Function의 합성과 Predicate의 결합

Function과 Predicate에 정의된 메서드

 

Function

default <V>Function<T,V> andThen(Function<? super R, ? extends V> after)

default <V>Function<V,R> compose(Function<? super V, ? extends T> before)

static <T> Function<T,T> identity()

 

andThen의 사용예시 Function f, g가 있을 때, f.andThen(g)는 함수 f를 적용한 후 g함수를 적용한다는 의미이다.

compose는 반대로 f.compose(g)의 경우 g함수를 먼저 적용한후 f함수를 적용한다.

identity()는 항등함수로 x->x 를 나타낸다. 잘 사용하지않는다.

 

Predicate

default Predicate<T> and(Predicate<? super T> other)

default Predicate<T> or(Prerdicate<? super T> other)

default Predicate<T> negate()

static <T> Predicate<T> isEqual(Object targetRef)

 

and, or 메서드는 내부에 또다른 조건식을 필요로한다. and(조건식)의 경우 두 조건식의 and결합(&&)이고 or(조건식)의 경우 두 조건식의 or결합(||)이다.

negate()는 부정연산자(!)가 결합되는것이다.

isEqual()은 매개변수로 대상하나를 지정하고 또다른 비교대상은 test()의 매개변수로 지정하는 방식으로 사용하는데 각각의 대상이 같은지 비교한다.

 

메서드참조

람다식을 더욱 간결하게 표현하는 방법으로 람다식이 하나의 메서드만 호출하는 경우 '메서드 참조(method reference)'라는 방법으로 람다식을 간략히 할 수 있다.

 

Function<String, Integer> f = (String s) -> Integer.parseInt(s);

위같은 함수형인터페이스가 정의되었다고 가정할 때, 위식을 아래와 같이 간단하게 나타낼 수 있다.

Function<String, Integer> f = Integer::parseInt;

람다식의 일부가 생략되었다. 생략된정보들은 좌변의 함수형인터페이스 형식으로부터 얻거나 우변의 parseInt메서드호출로 알 수 있다. Function<String, Integer>는 String을 매개변수로 받고 반환값은 Integer로 반환한다는 의미이다.

해당 Function의 메서드로는 Integer클래스의 parseInt를 사용하는것을 알 수 있다.

 

3가지 경우의 메서드 참조

static메서드 참조

(x) -> ClassName.method(x) == ClassName::method

인스턴스메서드 참조

(obj, x) -> obj.method(x) == ClassName::method

특정 객체 인스턴스 메서드 참조

(x) -> obj.method(x) == obj::method

 

생성자 매서드 참조

생성자를 호출하는 람다식도 메서드 참조로 변환할 수 있는데 위와같이 하면된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90

'Programming > JAVA' 카테고리의 다른 글

스트림(Stream)의 중간연산  (0) 2021.08.25
스트림(Stream)  (0) 2021.08.24
람다식(Lambda expression)  (0) 2021.08.21
fork & join  (0) 2021.08.20
쓰레드의 동기화  (0) 2021.08.20
728x90

람다식 : 메서드를 하나의 식(expression)으로 표현한 것. '익명 함수(anonymous function)'이라고도 한다.

 

람다식 작성

ex)

(매개변수 선언) -> {

   ...

}

 

람다식에서는 return이나 {}, 매개변수 타입등을 생략할 수 있다.

매개변수 타입의 경우 매개변수타입이 추론 가능한경우 생략이 가능하다.

{}내에 함수의 코드 즉 문장이 하나뿐인 경우 중괄호({})도 생략이 가능하다.

 

함수형 인터페이스(Functional Interface)

람다식은 메서드와같은 역할을 하지만 익명클래스의 객체와 같은것이다.

즉 람다식은 객체이다.

 

함수형 인터페이스를 통해 Lamdba 표현식을 사용한 경우이다.

람다식의 선언부와 함수형 인터페이스의 추상메서드의 선언부가 일치한다.

그렇기 떄문에 해당 함수를 정의하여 사용할 수 있으며 MyFunction2라는 함수형 인터페이스의 max함수가 람다식의 코드로 정의되는 것이다.

함수형 인터페이스는 오직 하나의 추상메서드만 정의되어야 한다는 제약이있다. 이러한 제약 덕분에 람다식과 인터페이스의 메서드가 1대1 매칭이 가능하다.

 

함수형 인터페이스 타입의 매개변수와 반환타입

메서드의 매개변수가 함수형 인터페이스 타입이라면, 메서드를 호출할 때 람다식을 참조하는 참조변수를 매개변수로 지정해주어야한다.

ex)

void aMethod(MyFunction f){

  f.myMethod();

}

 

MyFunction f = () -> System.out.println("myMethod()");

aMethod(f);

 

또는 참조변수 없이 람다식을 바로 대입해줄 수 있다.

aMethod(()-> System.out.println("myMethod()"));

 

또한 메서드의 반환타입이 함수형 인터페이스 타입이라면, 이 함수형 인터페이스의 추상메서드와 동등한 람다식을 가리키느 참조변수를 반환하거나 람다식을 직접 반환할 수 있다.

ex)

MyFunction myMethod(){

   MyFunction f = () ->{};

}

 

함수형 인터페이스를 통해 람다식을 바로 받아오거나, 메서드를 전달하는 등이 가능하다.

728x90

'Programming > JAVA' 카테고리의 다른 글

스트림(Stream)  (0) 2021.08.24
다양한 함수형 인터페이스 & 메서드 참조  (0) 2021.08.22
fork & join  (0) 2021.08.20
쓰레드의 동기화  (0) 2021.08.20
쓰레드의 상태 및 제어  (0) 2021.08.16
728x90

fork & join 프레임웤 : 하나의 작업을 작은 단위로 나눠서 여러 쓰레드가 동시에 처리하는것을 쉽게 만들어 준다.

수행할 작업에 따라 RecursiveAction, RecursiveTask 두 클래스중 하나를 상속받아 구현해야 한다.

 

RecursiveAction : 반환값이 없는 작업을 구현할때

RecursiveTask : 반환값이 있는 작업을 구현할 때

두 클래스 모두 compute()라는 추상메서드를 가지고있는데 상속을 통해 compute()메소드를 구현하고 작동시키면된다.

compute()를 구현한 후 쓰레드 풀과 수행할 작업(compute()가 구현된 객체)을 생성해 준다.

이후 start가 아닌 invoke()를 통해 작업을 시작한다.

ex)

ForkJoinPool pool = new ForkJoinPool();

SumTask task = new SumTask(from, to);

 

Long result = pool.invoke(task); //invoke()를 통해 생성한 작업시작

 

fork()와 join()

fork() : 작업을 쓰레드의 작업 큐에 넣는것이다. 작업큐에 들어간 작업은 더이상 나눌 수 없을때까지 나뉜다. 비동기 메서드이다.

join() : 해당 작업의 수행이 끝날 때까지 기다렸다가 수행이 끝나면 그 결과를 반환한다. 동기메서드이다.

 

rightSum.compute를 통해 계속 반으로 나눈 계산의 왼쪽 계산을 fork한다. 그리고 size가 5이하가되면 계산을 실행한다.

쓰레드를 많이사용한것이 시간은 더오래걸린다. 작업을 나누고 합치는데 시간이 걸리기 때문이다. 

항상 멀티쓰레드가 빠른것이 아니기 때문에 항상 테스트해보고 더빠를경우에만 멀티쓰레드를 사용하는것이 좋다.

728x90

'Programming > JAVA' 카테고리의 다른 글

다양한 함수형 인터페이스 & 메서드 참조  (0) 2021.08.22
람다식(Lambda expression)  (0) 2021.08.21
쓰레드의 동기화  (0) 2021.08.20
쓰레드의 상태 및 제어  (0) 2021.08.16
데몬 쓰레드(daemon thread)  (0) 2021.08.13

+ Recent posts