jhhan의 블로그

Java의 정석을 시작하자(5)(2/8) 본문

JAVA

Java의 정석을 시작하자(5)(2/8)

jhhan000 2020. 2. 14. 12:05

14장 스트림부터 이어서 진행한다.

스트림은 예제가 별로 없어서 이해하기 힘들 수도 있다.

-스트림

  • 데이터 소스를 추상화, 데이터를 다루는데 자주 사용되는 메서드 정의
  • 데이터 소스가 무엇이던 간에 같은 방식으로 이용가능하다.
  • 코드의 재사용성이 높아진다.

-스트림의 특징

  • 데이터 소스를 변경하지 않음
  • 일회용 : 한번 사용하면 다시 사용 불가능
  • 작업을 내부 반복으로 처리  ex) forEach()
  • 지연된 연산 : 중간 연산이 수행되지 않음
  • Stream<Integer>와 IntStream : IntStream이 더 효율적이다.
  • 병렬스트림 : 병렬 처리가 쉽다.

-스트림 만들기

  • 1. Collection에 stream()이 정의되어 있다.
    • List, Set은 모두 스트림을 생성할 수 있다.
    • Stream<T> Collection.stream()으로 생성
    • ex1) List<Integer> list = Arrays.asList(1,2,3,4,5,6);
    • ex2) Stream<Integer> intStream = list.stream();
  • 2. 문자열 스트림을 생성할 수 있다.
    • ex1) Stream<String> strStream = Stream.of("a","b","c");
    • ex2) Stream<String> strStream = Arrays.stream(new String[] {"a","b","c"});
  • 3. 기본형 배열을 소스로 하는 스트림
    • ex1) IntStream IntStream.of(int[])
    • ex2) IntStream Arrays.stream(int[])
  • 4. 난수를 생성하는 스트림
    • ex) IntStream ints() ,  DoubleStream doubles()
  • 5. 정 범위의 정수를 가지는 스트림 생성
    • ex1) IntStream intStream = IntStream.range(1,5);  // 1,2,3,4
    • ex2) IntStream intStream = IntStream.rangeClosed(1,5);  // 1,2,3,4,5
  • 6. 람다식을 매개변수로 받아 스트림 생성
    • static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
    • static <T> Stream<T> generate(Supplier<T> s)                 -> 이런 형식으로 만든다.
    • ex1) Stream<Double> randomStream = Stream.generate(Math::random);
    • ex2) Stream<Integer> OneStream = Stream.generate(()->1);
  • 7. 파일의 목록을 소스로 하는 스트림 생성
    • Stream<Path>  Files.list(Path dir)  -> 이런 식으로 만든다.
    • ex) Stream<String> Files.lines(Path path)
    • cf) 빈 스트림 : Stream emptyStream = Stream.empty();

-스트림의 연산

  • 스트림에 제공하는 연산 -> 복잡한 작업들이 간단해 질 수 있다.
  • 중간연산, 최종연산으로 분류할 수 있다.
  • 중간연산 : map(), flatMap()이 핵심
  • 최종연산 : reduce(), collect()가 핵심

-중간연산

  • 연산 결과가 스트림으로 반환 -> 중간연산은 연속해서 연결 가능
  • skip(), limit() : 스트림의 일부를 잘라낼 때 사용
  • filter() : 주어진 조건에 맞지 않는 요소 걸러냄
  • distinct() : 중복된 요소 제거
  • peek() : 연산과 연산 사이에 올바르게 처리되었는지 확인
  • sorted() : 정렬할 때 사용
  • Comparator의 메서드 : 이건 예제를 보자.

  • Comparator의 메서드에 대한 예제이다.
  • 총점별로 내림차순 정렬을 했다.

-map() : 원하는 필드만 뽑아내거나 특정 형태로 변환해야 할 때 사용

  • Stream<R> map(Function<? super T, ? extends R> mapper)
  • T타입을 R타입으로 변환해서 반환하는 함수 지정

map() 예제
결과물

-flatMap()

  • Stream<T[]>인 경우 Stream<T>로 변환해야 작업이 더 편리할 때 사용
  • map() 사용시 : Stream<String[]> -> Stream<Stream<String>>
  • flatMap() 사용시 : Stream<String[]> -> STream<String>

flatMap() 예제

** flatMap() 예제이다.

** 앞에 나왔던 중간연산들도 사용되었다.

 

 

 

 

-Optional<T> : T타입의 객체를 감싸는 래퍼클래스

  • Optional 객체 생성 : of() 혹은 ofNullable()을 이용한다.
  • Optional 객체의 값 가져오기 : get() 사용
  • Exception에 대비해서 orElse()도 사용한다.
  • orElseGet() : null을 대체할 값을 반환하는 람다식 지정
  • orElseThrow() : null일 때 지정된 예외 발생

Optional<T> 예제

-최종연산

  • 최종연산은 스트림의 요소를 소모해서 결과를 만들어낸다.
  • 연산 후에 더이상 스트림을 사용할 수 없다.
  • forEach() : 스트림의 요소를 출력하는 용도로 자주 쓰임
  • allMatch() : 모든 요소가 일치하면 참
  • anyMatch() : 하나의 요소라도 일치하면 참
  • noneMatch() : 모든 요소가 불일치하면 참
  • findFirst() : 조건에 일치하는 첫번째 요소 반환
  • findAny() : 조건에 일치하는 요소 1개 반환 - 병렬 스트림

-reduce() : 스트림의 요소를 줄여나가며 연산 수행

reduce() 예제

-collect() : 스트림의 요소를 수집하는 최종연산

  • collect() : 스트림의 최종연산. 매개변수로 컬렉터 필요함
  • Collector : 인터페이스. 컬렉터는 이 인터페이스 구현
  • Collectors : 클래스. static메서드로 미리 작성된 컬렉터 제공
  • 스트림의 모든 요소를 컬렉션에 수집 : toList() 사용
  • 스트림의 통계 : counting(), summingInt()
  • 스트림 리듀싱 : reducing()
  • 스트림을 문자열로 결합 : joining()

-스트림의 그룹화 & 분할

  • collect()가 필요한 이유를 가장 잘 보여주는 부분
  • 그룹화 : 특정 기준으로 묶는 것 ,  groupingBy() 사용
    • Collector groupingBy(Function classifier)
    • Collector groupingBy(Function classifier, Collector downstream)
    • Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)
  • 분할 : 스트림의 요소를 2가지로 나누는 것 , partitioningBy() 사용
    • Collector partitioningBy(Predicate predicate)
    • Collector partitioningBy(Predicate predicate, Collector downstream)
  • 예제가 꽤 긴 편이기 때문에 지금 당장은 올리지 못할 것 같다. 아마 안 올릴 수도 있다.

-partitioningBy()

  • 학생들을 분류하는 코드를 부분적으로 정리할 것이다.
  • Map<Boolean, List<Student>> stuBySex = stuStream.collect(partitioningBy(Student::isMale));
  • counting() - 학생 수 구하기
  • Map<Boolean, Long> stuNumBySex = stuStream.collect(partitioningBy(Student::isMale, counting()));
  • summingLong() - 총점 구해보기
  • Map<Boolean, Optional<Student>> topScoreBySex
  •    = stuStream.collect(partitioningBy(Student::isMale, maxBy(comparingInt(Student::getScore))));

-groupingBy()

  • 학생들을 반 별로 그룹짓기
  • Map<Integer, List<Student>> stuByBan = stuStream.collect(groupingBy(Student::getBan, toList()));
  • 위의 코드에서 toList()는 생략가능하다.
  • Map<Student.Level, Long> stuByLevel = stuStream
  •               .collect(groupingBy(s->{
  •                         if(s.getScore() >= 200)       return Student.Level.HIGH;
  •                         else if(s.getScore() >= 100) return Student.Level.MID;
  •                         else                               return Student.Level.LOW;
  •                    }, counting())
  •               );

-스트림의 변환 정리

from to 변환 메서드
스트림 기본형 스트림 mapToInt()
mapToLong()
mapToDouble()
기본형 스트림 스트림 boxed()
mapToObj()
기본형 스트림 기본형 스트림 asLongStream()
asaDoubleStream()
스트림 부분 스트림 skip()
limit()
두 개의 스트림 스트림 concat()
스트림의 스트림 스트림 flatMap()
flatMapToInt()
flatMapToLong()
flatMapToDouble()
스트림 병렬 스트림 parallel()
sequential()
스트림 컬렉션 collect()
컬렉션 스트림 stream()
스트림 Map collect()
스트림 배열 toArray()

 

15장부터는 다음 글에 올리겠다.