Chapter14 람다와 스트림 Lamda & stream

cookienc·2021년 7월 16일
0

Java 기본 개념

목록 보기
13/13
post-thumbnail

1. 람다식(Lamda expression)

1.1 람다식이란?

  • 함수(메서드)를 간단한 '식(expression)'으로 표현하는 방법
  • 익명 함수(이름이 없는 함수, anonymous function)
  • 함수와 메서드의 차이
    • 근본적으로는 동일. 함수는 일반적인 용어, 메서드는 객체지향개념 용어
    • 함수는 클래스에 독립적, 메서드는 클래스에 종속적

1.2 람다식 작성하기

  1. 메서드의 이름과 반환타입을 제거하고 '->'를 블록{} 앞에 추가한다.
  2. 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에 ';'안 붙임)
  3. 매개변수의 타입이 추론 가능하면 생략가능(대부분의 경우 생략가능)
  • 주의사항
    1. 매개변수가 하나인 경우, 괄호() 생략가능(타입이 없을 때만)
    2. 블록 안의 문장이 하나뿐 일 때, 괄호{} 생략가능(끝에 ';' 안 붙임)
    3. 단, 하나뿐인 문장이 return문일 경우 괄호{} 생략 불가

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

  • 람다식은 익명 함수가 아니라 익명 객체이다.
  • 람다식(익명 객체)을 다루기 위한 참조변수가 필요. 참조변수의 타입은 무엇으로? → 함수형 인터페이스로 다루어야 한다.
  • 함수형 인터페이스 : 단 하나의 추상 메서드만 선언된 인터페이스
@FunctionalInterface
interface MyFunction {
	public abstract int max(int a , int b);
}
/////////////////////////////////////////
My Function f = new MyFunction() {
			public int max(int a, int b) {
                    	   return a > b ? a: b ;
                   	}
			     	  };
////////////////////////////////////////
int value = f.max(3, 5); // OK. 

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

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

<람다식의 타입간 형변환>

  • 람다식은 익명 객체이기 때문에, 타입이 없다. → 대입 연산자를 사용하기 위해 형변환을 하는데, 생략이 가능하다.
  • 람다식은 객체이지만 Object타입으로 형변환 할 수 없다. → 굳이 하려면 함수형 인터페이스로 먼저 형변환 후에 형변환해야 한다.

<외부 변수를 참조하는 람다식>

  • 람다식 내에서 참조하는 지역변수는 final이 붙지 않아도 상수로 간주되기 때문에 변경 불가이다.
  • 외부 지역변수와 같은 이름의 람다식 매개변수는 허용되지 않는다.

1.4 java.util.function 패키지

  • 자주 사용하는 다양한 함수형 인터페이스를 제공. → 표준화 할 수 있다.

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

매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스

기본형을 사용하는 함수형 인터페이스

1.5 Function의 합성과 Predicate의 결합

  • Function의 결합
    • Function타입의 두 람다식을 하나로 합성 – andThen( )
    • Function타입의 두 람다식을 하나로 합성 – compose( )
  • Predicate의 결합
    • and(), or(), negate()로 두 Predicate를 하나로 결합(default메서드)
    • 등가비교를 위한 Predicate의 작성에는 isEqual()를 사용(static메서드)

1.6 메서드 참조(method reference)

  • 하나의 메서드만 호출하는 람다식은 '메서드 참조'로 간단히 할 수 있다.
  • static메서드 참조

<생성자의 메서드 참조>

  • 생성자와 메서드 참조


2. 스트림(stream)

2.1 스트림이란?

  • 다양한 데이터 소스(컬렉션, 배열)를 표준화된 방법으로 다루기 위한 것
  • 스트림이 제공하는 기능
    • 중간 연산 - 연산결과가 스티림인 연산. 반복적으로 적용가능
    • 최종 연산 - 연산결과과 스트림이 아닌 연산. 단 한번만 적용가능(스트림의 요소를 소모)
  • 스트림의 특징
    • 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않는다.
    • Iterator처럼 일회용이다.(필요하면 다시 스트림을 생성해야하 함)
    • 최종 연산 전까지 중간연산이 수행되지 않는다. - 지연된 연산
    • 작업을 내부 반복으로 처리한다.
    • 작업을 병렬로 처리 - 병렬스트림
    • 기본형 스트림 - IntStream, LongStream, DoubleStream
      • 오토박싱&언박싱의 비효율이 제거됨(Stream< Integer>대신 IntStream사용)
      • 숫자와 관련된 유용한 메서드를 Stream보다 더 많이 제공

<스트림의 연산>

  • 중간연산
    • 최종연산

2.2 스트림 만들기

<컬렉션>

  • Collection인터페이스의 stream()으로 컬렉션을 스트림으로 변환
    Stream<E> stream() // Collection에 stream()이 정의되어 있다. → List와 Set을 구현한 컬렉션 클래스들은 모두 이 메서드로 steram 생성 가능

<배열>

  • 객체 배열로부터 스트림 생성하기
    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 IntStream.of(int... valuse)
    IntStream IntStream.of(int[])
    IntStream Arrays.stream(int[])
    IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)

<임의의 수>

  • 난수를 요소로 갖는 스트림 생성하기
    • 지정된 범위의 난수를 요소로 갖는 스트림을 생성하는 메서드(Random클래스)
  • 특정 범위의 정수를 요소로 갖는 스트림 생성하기(IntStream, LongStream)

<람다식 iterate(), generate()>

  • 람다식을 소스로 하는 스트림 생성하기
// 무한 스트림
static <T> Steram<T> iterate(T seed, UnaryOpearator<T> f) // 이전 요소에 종속적, seed : 초기 값
static <T> Steram<T> generate(Supplier<T> a) // 이전 요소에 독립적
  • iterate()는 이전 요소를 seed로 해서 다음 요소를 계산한다.
  • generate()는 seed를 사용하지 않는다.

<파일과 빈 스트림>

  • 파일을 소스로 하는 스트림 생성하기
    `Stream Files.list(Path dir) // Path는 파일 또는 디렉토리
    Stream<String> Files.lines(Path path) // 파일 내용을 라인 단위로 Stream<string>으로 바꿈
    Stream<String> Files.lines(Path path, Charset cs)
    Stream<String> lines() // BufferedReader클래스의 메서드
  • 비어 있는 스트림 생성하기
    Stream emptyStream = Stream.empty(); // empty()는 빈 스트림을 생성해서 반환한다.
    long count = emptyStream.count(); // count의 값은 0

2.3 스트림의 중간연산

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

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

<스트림 정렬하기 - sorted()>

  • Comparator의 comparing()으로 정렬 기준을 제공
    comparing(Function<T, U> keyExtractor)
    comparing(Function<T, U> keyExtractor, Comparator<U> keyComparator)
    ////////////////////////////////////////////////////
    studentStream.sorted(Comparator.comparing(Student::getBan) // 반별로 정렬
    				.forEach(System.out::println);
  • 추가 정렬 기준을 제공할 때는 thenComparing()을 사용
    thenComparing(Comparator<T> other)
    thenComparing(Function<T, U> keyExtractor)
    thenComparing(Function<T, U> keyExtractor, Comparator<U> keyComp)
    //////////////////////////////////////////////////////////////
    studentStream.sorted(Comparator.comparing(Student::getBan) // 반별로 정렬
    				.thenComparing(Student::getTotalScore) // 총점별로 정렬
      			.thenComparing(Student::getName) // 이름별로 정렬
    				.forEach(System.out::println);

<스트림의 요소 변환하기 - map()>

<스트림의 요소를 소비하지 않고 엿보기 - peek()>

< mapTolnt(), mapToLong(), mapToDouble() >

  • Stream< T >타입의 스트림을 기본형 스트림으로 변환할 때 사용
    • Stream< String > IntStream 변활할 때, mapToInt(Integer::parseInt)
    • Stream< Integer > IntStream 변활할 때, mapToInt(Integer::intValue)

<스트림의 스트림을 스트림으로 변환 - flatMap()>

2.4 Optional< T >와 OptionalInt

<Optional< T >>

T타입 객체의 래퍼클래스

public final class Optional<T> {
	private final T value; // T타입의 참조변수(모든 종류의 객체, null까지도 저장 가능) -> 간접적으로 null를 다룸으로써 위험성 감소
		...
}

<Optional < T >객체 생성하기>

  • null대신 빈 Optional< T >객체를 사용하자
    Optional<String> optVal = null; // 널로 초기화. 바람직하지 않음
    Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화(<String> 생략가능)

<Optional객체의 값 가져오기 - get(), orElse(), orElseGet(), orElseThrow()>

  • isPresent() – Optional객체의 값이 null이면 false, 아니면 true를 반환

< OptionalInt, OptionalLong, OptionalDouble >

  • 기본형 값을 감싸는 래퍼클래스(성능 때문에 쓴다)
    • OptionalInt의 값 가져오기 - int getAsInt()
    • 빈 Optionial객체와의 비교

2.5 스트림의 최종 연산

< forEach() >

  • 스트림의 모든 요소에 지정된 작업을 수행 - forEach(), forEachOrdered()

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

  • 조건 검사
  • 조건에 일치하는 요소 찾기

< 리듀싱 - reduce() >

  • 스트림의 요소를 하나씩 줄여가며 누적(accumulator)연산 수행
    • 매개변수의 타입이 BinaryOperator< T > → 처음 두 요소를 가지고 연산한 결과를 가지고 그 다음 요소와 연산

2.6 collect()

  • collect()는 Collector를 매개변수로 하는 스트림의 최종연산
    • reduce() : 전체 리듀싱
    • collect() : 그룹별 리듀싱
  • Collector는 수집(collect)에 필요한 메서드를 정의해 놓은 인터페이스
  • Collectors클래스는 다향한 기능의 컬렉터(Collector를 구현한 클래스)를 제공
  • collect() : 스트림의 최종연산, 매개변수로 컬렉터를 필요로 한다.
  • Collector : 인터페이스, 컬렉터는 이 인터페이스를 구현해야한다.
  • Collectors : 클래스, static 메서드로 미리 작성된 컬렉터를 제공한다.

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

  • 스트림을 컬렉션으로 변환 - toList(), toSet(), toMap(), toCollection()
  • 스트림을 배열로 변환 - toArray() ( 배열의 반환타입을 정하지 않으면 Object[] 타입으로 반환)
    Student[] stuNames = studentStream.toArray(Student[]::new); // OK
      Student[] stuNames = studentStream.toArray(); // 에러
      Object[] stuNames = studentStream.toArray() ; // OK

<스트림의 통계 - counting( ), summingInt( )>

  • 스트림의 통계정보를 제공 - counting( ), summingInt( ), maxBy( ), minBy( ), …

<스트림을 리듀싱 - reducing>

  • 그룹별로 리듀싱이 가능

<문자열 스트림의 요소를 모두 연결 - joining()>

<스트림의 그룹화와 분할 - groupingBy(), partitioningBy()>

  • partitioningBy()는 스트림의 요소를 2분할 한다.
  • groupingBy()는 스트림의 요소를 n분할 한다.

0개의 댓글