[JAVA] Stream - 1

Coastby·2022년 11월 22일
0

JAVA

목록 보기
31/33

Stream

  • 다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것
    • Collection이나 배열을 다루기 위해 Collection이나 Iterator 같은 인터페이스를 이용했으나, 통일되지 않고 너무 길다는 문제점이 있었다.
  • JDK 1.8 이후에 stream으로 통일되었다.
  • 데이터 소스를 추상화하고, 자주 사용되는 메서드를 정의해 놓았다. → 어떤 데이터소스든 같은 방식으로 다루며, 코드 재사용성이 높아졌다.
  • 스트림 사용 순서 1️⃣ 스트림 만들기 → 2️⃣ 중간 연산 → 3️⃣ 최종 연산

○ 스트림의 연산

스트림에 정의된 메서드 중에서 데이터 소스를 다루는 작업을 수행하는 것을 연산 (Operation)이라고 한다.

스트림이 제공하는 연산은 중간 연산과 최종 연산으로 분류할 수 있는데, 중간 연산은 연산 결과를 스트림으로 반환하기 때문에 연속해서 연결할 수 있다. 반면, 최종 연산은 스트림의 요소를 소모하면서 연산을 수행하므로 단 한번만 연산이 가능하다.

💡 중간 연산
: 연산 결과가 스트림인 연산, 스트림에 연속해서 중간 연산할 수 있음
최종 연산
: 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한번만 가능

스트림의 특징

  1. 스트림은 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않는다. (Read Only)

  2. 스트림은 Iterator처럼 일회용이다.

    • 필요하면 다시 스트림을 생성해야 한다.
  3. 최종 연산 전까지 중간연산이 수행되지 않는다. - 지연된 연산

    • 아래의 코드는 난수를 생성하는 무한스트림이다. 그런데 이를 중간 연산이 가능한 것은 지연된 연산을 하기 때문이다.
    • 메서드가 호출되었을 때 바로 실행되는 것이 아니라, 어떤 작업을 하는지 봐뒀다가 최종 연산이 수행되어야 비소로 스트림의 중간 연산을 거쳐 최종연산에서 소모된다.
    IntStream intStream = new Random().ints(1, 46);
            intStream.distinct().limit(6).sorted()
                    .forEach((i->System.out.print(i+", ")));
  4. 스트림은 작업을 내부 반복으로 처리한다.

    • for문을 한 줄로 만들 수 있다. : forEach안에 for문이 들어가 있다.
stream.forEach(System.out::println);
  1. 스트림의 작업을 병렬로 처리 - 병렬스트림
    • 기본형이 sequantial() : parallel()을 호출한 것을 취소할 때만 사용한다.
    • 병렬처리가 항상 더 빠른 결과를 얻게 해주는 것은 아니다.
int sum = strStream.**parallel()**      //병렬스트림으로 전환
						.mapToInt(s -> s.length()).sum();    //모든 문자열 길이의 합
  1. 기본형 스트림 - IntStream, LongStream, DoubleStream
    • 오토박싱 & 언박싱의 비효율이 제거됨 (Stream 대신 IntStream 사용)
    • 숫자와 관련된 유용한 메서드를 Stream보다 더 많이 제공
    • 성능을 개선하고 싶을 때 사용한다.

스트림 만들기

○ Collection

Collection에는 stream()이 정의되어 있다. 그래서 Collection의 자손인 List와 Set을 구현한 컬렉션 클래스들은 모두 이 메서드로 스트림을 생성할 수 있다.

Stream<T> Collction.stream()
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> intStream = list.stream();

○ 배열

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

Stream<T> Stream.of(T1, T2, ...)    //가변인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.Stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive)    //일부
  • 예시
    Stream<String> strStream = Stream.of("a", "b", "c");
    Stream<String> strStream = Stream.of(new String[]{"a", "b", "c"});
    Stream<String> strStream = Arrays.stream(new String[]{"a", "b", "c"});
    Stream<String> strStream = Arrays.stream(new String[]{"a", "b", "c"}, 0, 3);
IntStream IntStream.of(1, 2, ..)
IntStream IntStream.of(int[])
IntStream Arrays.Stream(int[])

○ 특정 범위의 수

지정된 범위의 연속된 정수를 스트림으로 생성

IntStream IntStream.range(int begin, int end)  //end 불포함
IntStream IntStream.rangeClosed(int begin, int end) //end 포함
  • 예시
    IntStream.range(1, 5).forEach(System.out::println);
    
    //결과
    1
    2
    3
    4

○ 임의의 수

Random 클래스에는 아래와 같은 인스턴스 메서드들이 있다. 이 메서드들은 해당 타입의 난수들로 이루어진 스트림을 반환한다.

IntStream ints()
LongStream longs()
DoubleStream doubles()

위 메서드들은 스트림의 크기가 정해지지 않은 ‘무한 스트림(Infinite stream)’이므로 limit()으로 크기를 제한하거나, 매개변수로 스트림의 크기를 지정해야 한다.

Int Stream intStream = new Random().ints();    //무한 스트림
IntStream.limit(5).forEach(System.out::println);
IntStream ints(long streamSize)
LongStream longs(long streamSize)
DoubleStream doubles (long streamSize)

생성된 스트림은 아래와 같은 범위를 가지지만, 지정된 범위의 난수를 발생시킬 수도 있다.

IntStream ints(int begin, int end)    //무한 스트림
IntStream ints(long streamSize, int begin, int end)    //유한 스트림

○ 람다식 - iterate(), generate()

  • 람다식을 소스로 하는 스트림 생성하기. 무한 스트림이라 limit()으로 잘라줘야한다.
  • 기본형 스트림 타입의 참조변수로 다룰 수 없다.
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
static <T> Stream<T> generate(Supplier<T> s)
  • iterate()는 이전 요소를 Seed로 해서 다음 요소를 계산한다.
//0, 2, 4, 6,...
Stream<Integer> evenStream = Stream.iterate(0, n->n+2);
  • generate()는 seed를 사용하지 않는다.
    • 매개변수가 없는 람다식만 혀용된다.
//lambdda    
Stream<Double> randomStream = Stream.generate(Math::random);
//1, 1, 1,...
Stream<Integer> oneStream = Stream.generate(()->1);

○ 파일

  • 파일을 소스로 하는 스트림 생성하기
  • java.nio.file.Files는 파일을 다루는데 필요한 유용한 메서드들을 제공하는데, list()는 지정된 디렉토리에 있는 파일의 목록을 소스로 하는 스트림을 생성해서 반환한다.
Stream<Path> Files.list(Path dir)    //Path는 파일 또는 디렉토리
  • 파일의 한 행(line)을 요소로 하는 스트림을 생성하는 메서드들도 있다.
    • BufferedReader 클래스의 메서드는 파일 뿐만 아니라 다른 입력대상으롭터도 데이터를 행단위로 읽어올 수 있다.
Stream<String> Files.lines(Path path)
Stream<String> Files.lines(Path path, Charset cs)
Stream<String> lines()      //BufferedReader 클래서의 메서드 

○ 비어있는 스트림

Stream emptyStream = Stream.empty();
long count = emptyStream.count();    //count 값은 0

○ 두 스트림의 연결

Stream의 static 메서드인 concat()을 사용하면 두 스트림을 하나로 연결할 수 있다.

profile
훈이야 화이팅

0개의 댓글