코드스테이츠 18일차

안형준·2022년 5월 19일
0

코드스테이츠

목록 보기
18/32
post-thumbnail

1차 학습목표

람다식 정리
스트림의 특징과 사용 목적을 이해할 수 있다.
컬렉션과 배열로부터 스트림을 만들 수 있다.
스트림의 주요 메서드를 활용해 필요한 연산을 수행할 수 있다.

👻람다식이란? 람다식 작성하기
- 람다식 : 함수(메서드)를 간단한 식(expression)으로 표현하는 방법

- 함수와 메서드의 차이 : 
-> 근본적으로 동일, 함수는 일반적 용어, 메서드는 객체지향개념 용어
-> 함수는 클래스에 독립적, 메서드는 클래스에 종속적

- 람다식 작성방법 : 
1) 메서드의 이름, 반환타입 제거 후에 '->'를 블록 앞에 추가, 익숙해지며 자연스럽게 사용할 수 있도록 숙달
2) 반환값 있는 경우, 식이나 값만 적고 return문 생략 가능(;함께 생략)
3) 매개변수와 타입이 추론 가능 시 생략가능(대부분의 경우 생략가능)

- 주의사항 :
1) 매개변수가 하나일 시 -> 괄호 생략가능
2) 블록 안의 문장 하나뿐 -> 괄호 생략가능
3) 단, 하나뿐인 문장이 return문이면 괄호() 생략불가

- 람다식은 사실은 익명함수이 아닌 익명객체이다.

👻함수형 인터페이스
함수형 인터페이스 @FunctionalInterface
1.함수형 인터페이스는 단 하나의 추상메서드만 가져야 한다 
2. 람다식(익명 객체)을 다루기 위한 참조변수의 타입은 함수형 인터페이스로 한다. 
예) 익명 객체를 람다식으로 대체
3. 람다식을 매개변수로 받고, 리턴타입으로 사용할 수 있다. 
추상메서드를  통해서 람다식을 호출한다고 생각하기 

👻java.util.function 패키지
- java.util.function 패키지 : 자주 사용하는 다양한 함수형 인터페이스 제공(표준화)
* Runnable : 매개변수 x 반환값 x
* Supplier<T> : 매개변수 x 반환값 o
* Consumer<T> : Supplier와 반대로 매개변수 o 반환값 x
* Function<T, R> : 일반적 함수 하나의 매개변수 o 반환값 o
* Predicate<T> : 조건식을 표현하는데 사용, 매개변수 하나 반환 타입 boolean
* BiConsumer<T,U> : 두 개의 매개변수 반환값 x
* BiPredicate<T,U> : 조건식을 표현하는데 사용 매개변수 둘, 반환값 boolean
* BiFunction<T,U,R> : 두 개의 매개변수를 받아서 하나의 결과 반환

👻Predicate의 결합, 컬렉션 프레임워크와 함수형 인터페이스
- andThen() : 두 함수의 합성함수
- compose() : andThen()의 합성과는 반대로 합성

- Collection : removeIf(Predicate<E> filter) -> 조건에 맞는 요소 삭제
- List : 
replaceAll(UnaryOperator<E> operator) -> 모든 요소 변형 후 대체
- Iterable : 
forEach(Consumer<T> action) -> 모든요소에 action 수행
- Map : 
compute(K key, BiFunction<K,V,V> f) -> 지정된 키값에 f 수행
computeIfAbsent(K key, Function<K,V> f) -> 키가 없으면 작업 f 수행 후 추가
computeIfPresent(K key, BiFunction<K,V,V> f) -> 지정된 키가 없을 때 작업 f 수행
merge(K key, V value, BiFunction<K,V,V> f) -> 모든 요소에 병합작업 f 수행
forEach(BiConsumer<K,V> action) -> 모든 요소에 작업 action 수행
replaceAll(BiFunction<K,V,V> f) -> 모든 요소에 치환작업 f 수행

👻메서드 참조, 생성자의 메서드 참조
- 메서드 참조
->람다식을 더 간단히 한 것(클래스이름 :: 메서드이름)
- 생성자의 메서드 참조
-> Supplier<MyClass> s = () -> new MyClass();
-> Supplier<MyClass> s = MyClass::new;
-> Function<Integer,MyClass> s = (i) ->new MyClass(i);
-> Function<Integer, MyClass> s = MyClass::new;

-배열과 메서드 참조
-> Function<Integer, int[]> f = x-> new int[x];
-> Function<Integer, int[]> f = int[]::new;

👻스트림, 스트림의 특징
- 스트림의 기능 : 중간연산, 최종연산
-> 중간연산 : 연산결과가 스트림, 반복적으로 적용가능(0~n번)
-> 최종연산 : 연산결과가 스트림이 아닌 연산, 단 한번만 적용가능(스트림의 요소를 소모, 0~1번)

- 스트림의 특징 : 원본변경 안함(readonly), 일회용(like iterator 필요하면 다시 생성), 최종연산 전까지 중간연산이 수행되지 않음, 일종의 표시만 해둠(지연된 연산)

- 스트림의 작업을 병렬로 처리 : 병렬스트림 parallel()

- 기본형 스트림 : IntStream, LongStream, DoubleStream
-> 오토박싱&언박싱의 비효율 제거(Stream<Integer> -> IntStream)
-> 숫자와 관련된 유용한 메서드 Stream<T>보다 더 많이 제공

👻스트림 만들기
- 스트림 만들기 - 컬렉션
-> Collection interface의 stream()으로 컬렉션을 스트림으로 연결
-> Stream<E> stream() // Collection인터페이스의 메서드

- 스트림 만들기 - 배열
-> 객체배열로 부터 스트림 생성하기 : 
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 endInclusive)
-> 기본형 배열로부터 스트림 생성 :
IntStream IntStream.of(int... values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endInclusive)

- 스트림 만들기 - 임의의 수 : 
-> 난수를 요소로 갖는 스트림 생성 (ints()등)
-> 지정한 범위의 난수를 요소로 갖는 스트림을 생성하는 메서드(Random클래스)

- 스트림 만들기 - 특정 범위의 정수 :
-> 특정 범위의 정수를 요소로 갖는 스트림 생성(IntStream, LongStream)

- 스트림 만들기 - 람다식, Iterate(), generate() (무한 스트림)
-> 람다식을 소스로 하는 스트림 생성
-> Iterate()는 이전 요소를 seed로 해서 다음 요소를 계산
-> generate()는 seed를 사용하지 않는다.

👻스트림의 연산
- 스트림의 연산 : 
-> 스트림이 제공하는 기능 -> 중간 연산과 최종 연산
-> 중간연산 : 연산결과가 스트림, 반복적 사용 가능
-> 최종연산 : 연산결과가 스트림이 아닌 연산, 단 한번만 적용가능(스트림의 요소를 소모)
stream.distinct().limits(5).sorted().forEach(System.out::println); // forEach는 최종 나머지는 중간

- 스트림의 연산 - 최종연산 : 
-> Optional<T> -> 최소 요소 하나를 반환(null처리 방법 중 하나)
-> reduce() & collect() : 핵심 최종 연산, reduce()를 잘 이해해보자
-> reduce() : 스트림의 요소를 하나씩 줄여가면서 계산(리듀싱)
-> collect() : reduce()를 이용해서 grouping

👻스트림의 중간연산
- 스트림의 중간연산 : 
* 스트림 자르기 : skip(), limit()
* 스트림 요소 걸러내기 : filter(), distinct()
(중간연산이기 때문에 여러번 사용가능)
* 스트림 정렬하기 : sorted()
    - 정렬 시 필요한 것들 : 1) 정렬대상 2) 정렬기준

- Comparator의 comparing()으로 정렬기준 제공 :
-> comparing(Function<T, U> keyExtractor)
-> comparing(Function<T, U> keyExtractor,  Comparator<U> keyComparator)
(return type : Comparator)

- 추가 정렬 기준을 제공할 시 -> thenComparing() 이용 :
-> thenComparing(Comparator<T> other)
-> thenComparing(Function<T, U> keyExtractor)
-> thenComparing(Function<T, U> keyExtractor, Comparator<U> keyComp)

- 스트림의 중간연산 II ( map(), peek() - forEach와 비슷한 중간연산, flatmap() ) : 
-> 스트림의 요소 변환 : map() 사용
ex) 파일 스트림에서(Stream<File>) 파일 확장자를 중복없이 뽑아내기(예제참고)
-> 스트림의 요소를 소비하지 않고 엿보기 : peek() 사용
ex) 중간중간에 작업결과 확인 시 사용(디버깅용)
-> 스트림의 스트림을 스트림으로 변환 : flatMap()

👻Optional<T>
- Optional<T> : T 타입 객체의 Wrapper Class
public final class Optional<T> {
    private final T value;  // T 타입의 참조변수
                ....
}
- Optional을 다루는 이유
1) Null을 직접다루는 건 위험 : NullPointerException발생 위험
2) Null체크를 꼭 해줘야 함 : if문 필수, 코드가 지저분해 짐
-> 해당 처리와 관련된 다른 처리 방법 : 빈 배열, 빈 문자열로 초기화(Null을 직접 다루는 위험성을 회피하기 위함)

- Optional<T> 객체 생성하기 :
String str = "abc";
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(null);
Optional<String> optVal = Optional.ofNullable(null);

- null대신 빈 Optional<T> 객체를 활용하자 :
Optional<String> optVal = null;
Optional<String> optVal = Optional.<String> empty();

👻스트림의 최종연산
- 스트림의 최종연산 - reduce()
-> 스트림의 요소를 하나씩 줄여가며 누적연산 수행 - reduce()
Optional<T> reduce(BinaryOperator<T> accumulator)
T		 reduce(T identity, BinaryOperator<T> accumulator)
U		 reduce(T identity, BiFunction<U, T, U> accumulator, BinaryOperator<U> combiner)

** identity : 초깃값, accumulator : 이전 연산결과와 스트림의 요소에 수행할 연산, combiner : 병렬처리된 결과를 합치는데 사용할 연산 (병렬 스트림)

오늘은 람다식, 스트림에 대해 학습했다.
정말이지 내가 지금까지 배운건 뭐지?란 생각이 들 정도로 너무나도 새로운 개념이였고, 이해하기 어려웠다.
정리를 하면서 공부했음에도 정확하게 이해를 하기 어려웠고, 앞으로 반복적으로 영상 시청하면서 복습할 생각이다.
꾸준히 하다보면 익숙해지는 날이 올 수 있을 것이라고 믿는다.(제발)
오늘도 고생했고 내일도 파이팅!

profile
개발 공부

0개의 댓글