Java 8 : 스트림 활용

600g (Kim Dong Geun)·2021년 4월 6일
0

스트림 활용

필터링

  • 스트림을 요소로 선택하는 방법, Predicate로의 필터링고유요소로의 필터링이 있다.

프레디케이트로 필터링 - filter

  • filter 메소드는 Preicate를 인수로 받아서, 일치하는 모든 요소를 포함하는 스트림을 반환한다.
List<Dish> vegetarianMenu = menu.stream()
  															.filter(Dish::isVegetarian)
  															.collect(toList());

고유 요소 필터링 - distinct

  • 스트림은 고유 요소로 이루어진 스트림을 반환하는 distinct 메소드 지원.
List<Integer> numbers = Arrays.asList(1,2,3,4,5,3,3,2,4);
numbers.stream()
  .filter(i -> i%2 == 0)
  .distinct()
  .forEach(System.out::println);

스트림 슬라이싱

  • 요소를 선택하거나 스킵하는 다양한 방법

TAKEWHILE 활용

  • JAVA9 에서 추가된 메소드
  • Predicate를 이용하여, 참일때는 통과 시키고 Predicate를 만족하지 않을때 이후의 스트림을 버림.
  • 주로 정렬된 객체에서 사용
  • 스트림이 클 때, filter 보다 효율적으로 작용 (필터는 모두 순환)

DROPWHILE

  • JAVA9 에서 추가된 메소드
  • TAKEWHILE 과 쓰임새가 비슷하나, 처음으로 거짓이 되는 지점까지 발견된 요소를 버림

스트림 축소 - limit

  • 처음 n개의 요소만 반환

건너뛰기 - skip

  • 처음 n개 요소를 제외한 스트림을 반환

매핑

  • 특정 데이터를 선택하고 데이터를 처리하는데 사용되는 연산
  • Stream API는 mapflatMap 메소드를 지원

스트림의 각 요소에 함수 적용하기 - map

  • 인수로 제공된 함수는 각 요소에 적용되며, 함수를 적용한 결과가 새로운 요소로 매핑
  • 값을 고친다라는 개념보다는 새로운 버전을 만든다 라는 개념에 가까움.
List<String> dishNames = menu.stream()
  .map(dish::getName) // Menu 객체가 아닌, map을 이용해 String 객체만 뽑아냄
  .collect(toList());

스트림 평면화 - flatMap

  • 배열로된 스트림을 별도의 스트림으로 생성
String[] words = {"Hello","World"}
List<Integer> words = stream()
  .map(word -> word.split("")) //{"H","E","L","L","O","W","O","R","L","D"}
  .distinct()									//{"H","E","L","O","W","R","D"}
  .collect(toList());					//{}"HELLOWRD"}

검색과 매칭

  • 특정 속성이 데이터 집합에 있는지 여부를 검색하는 메소드
메소드설명사용법
anyMatch(Preicate<T> p )Preicate가 주어진 스트림에서 적어도 한 요소와 일치하는지 확인할 때 사용menu.stream().anyMatch(Dish::Vegetarian)
allMatch(Predicate<T>)Predicate가 주어진 스트림에서 모든 요소와 일치하는지 검사menu.stream().allMatch(Dish::Vegetarian)
noneMatch(Predicate<T>)Predicate가 주어진 스트림에서 모든 요소와 일치하지 않는지 검사사용법 같음
findAny(Predicate<T>)Predicate가 주어진 스트림에서 만족하는 요소를 찾는 즉시 반환하고 종료
findFirst(Predicate<T>)Preicate가 주어진 스트림에서 만족하는 요소 중 첫번째 요소를 반환

쇼트서킷 평가

전체 스트림을 처리하지 않았더라도 스트림의 결과를 반환하는 것. 원하는 요소를 찾았으면 즉시 결과를 반환하여 속도를 높이는 유용한 연산이고 allMatch,noneMatch,findFirst,findAny 등의 연산이 해당 기법을 사용한다.

findAny(), findFirst()?

findAny(), findFirst() 중 둘 중 무엇을 사용해도 상관이 없다면 findAny()를 사용한다. 이유는 병렬성 때문이다. 병렬실행에서는 첫 번째 요소를 찾기 어렵다. 따라서 요소의 반환 순서가 상관없다면 병렬 스트림에서는 제약이 적은 findAny를 사용한다.

리듀싱

  • 메뉴의 모든 칼로리의 합계를 구하시오, 메뉴에서 가장 칼로리가 높은 요리는? 과 같이 스트림 요소를 조합해서 더 복잡한 질의를 표현하는 방법
  • 함수형 프로그래밍 언어 용어로는 이과정이 마치 종이를 작은 조각이 될 때 까지 접는 것과 비슷하다는 의미로 폴드(Fold) 라고한다.

요소의 합

  • reduce는 두 개의 인수를 갖는다.
    • 초기값, 두 요소를 조합해서 새로운 값을 만드는 BinaryOperator<T>
int sum = numbers.stream().reduce(0 , (a,b) -> a+ b);
  • 초기값을 받지 않도록 오버로드된 reduce도 있다.
Optional<Integer> sum = numbers.stream().reduce((a,b)-> a+b);

다만 Optional을 사용하여 null 일 상황을 방지하여 주자.

최댓값과 최솟값

  • 이 또한 두 개의 인수를 가지고 활용할 수 있다.
    • 초기값, 스트림의 두 요소를 하나로 합치는데 사용할 람다
Optional<Integer> max = numbers.stream().reduce(Integer::max);

Optional<Integer> min = numbers.stream().reduce(Integer::min);

reduce를 사용하여 연산시 내부 구현에서 병렬로 reduce를 실행할 수 있다.

병렬로 reduce를 이용하기 위해서는 해당 변수가 공유되는 환경이기 때문에 동기화를 시켜야 한다. 동기화를 시키기 위해서 결국 병렬화로 얻어야 할 이득이 스레드 간의 소모적 경쟁 때문에 상쇄되어 버림. 따라서 해당 부분은 입력을 분할하고 분할된 입력을 더한다음 다시 합쳐야 한다. 해당 부분은 포크/조인 프레임워크를 이용하여 해결할 수 있다.

profile
수동적인 과신과 행운이 아닌, 능동적인 노력과 치열함

0개의 댓글