본문 바로가기
JAVA/Java Study

[Java] 스트림 API (Stream API)

by ♡˖GYURI˖♡ 2024. 6. 24.

스트림 API란?

여러 종류의 데이터를 다양한 방식으로 다룰 수 있도록 제공하는 표준화된 방법 (≥ JDK 1.8)

  • 배열이나 컬렉션 뿐만 아니라 파일 데이터도 가능
  • 반복문이나 반복자(iterator)를 사용하여 개발하지 않아도 되도록 지원
import java.util.Arrays;

public class BasicStreamExample {
	public static void main(String[] args) {
    	String[] stringArray = {"this", "is", "a", "stream", "example", "is", "this"};
        Arrays.stream(stringArray).distinct().forEach(System.out::println);
        
        int[] numArray = {1, 2, 3, 4, 5, 6, 1, 2, 3};
        int sum = Arrays.stream(numArray).distinct().sum();
        System.out.println(sum);
    }
}
  • distinct : 중복 제거

 

 

스트림 API 특징

  • 원본 데이터 변경 불가능
  • 일회용
  • 병렬 처리 가능
  • 내부 반복으로 작업 처리
  • 기본 데이터형을 처리할 수 있는 래퍼 스트림 지원 (Int/Double/LongStream)

 

 

스트림 API 연산 과정

스트림 생성 → 중개 연산(스트림의 변환) → 최종 연산(스트림의 사용)

스트림 생성 중개 연산 최종 연산
Array Filtering Calculating
List Mapping Collecting
File Sorting Reducing
Random Splitting Matching
etc. Iterating Iterating

 

 

스트림 생성

스트림을 사용하기 위해서는 아래의 여러 원본 데이터를 이용해 스트림 형태로 만들어야 함

  • 컬렉션, 배열
  • 가변 매개변수
  • 지정된 범위의 연속된 정수
  • 난수들
  • 람다 표현식
  • 파일
  • 빈 스트림
import java.util.*;

public class CreationStreamExample {
	public static void main(String[] args) {
    	// 배열에서 스트림 생성
        String[] arr = new String[]{"a", "b", "c", "d"};
        Stream<String> arrayToStream = Arrays.stream(arr);
        
        // Collection에서 스트림 생성
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        Stream<Integer> listToStream = list.stream();
        
        // 지정된 범위의 연속된 정수에서 스트림 생성
        IntStream continuousIntStream = IntStream.rangeClosed(1, 4);
        
        // 특정 타입의 난수로 이루어진 스트림 생성
        IntStream randomStream = new Random().ints(4);
        
        // 람다를 이용한 스트림 생성 generate
        Stream<Integer> lambdaIterate = Stream.iterate(0, n -> n + 1);
        Stream<Double> lambdaGenerateStream = Stream.generate(Math::random);	//error
        
        // 파일 스트림 생성
        try {
        	Stream<String> fileStream = Files.lines(Paths.get("path"));
        } catch (IOException e) {
        	throw new RuntimeException(e);
        }
        
        // 빈 스트림 생성
        Stream emptyStream = Stream.empty();
    }
}

 

 

스트림 중개 연산

앞에서 생성된 초기 스트림을 전달받아 필터링이나 변환 등의 연산을 수행

  • Filtering
  • Mapping
  • Sorting
  • Splitting
  • Iterating

 

Filtering

  • filtering() : 주어진 조건에 참인 요소를 필터링
  • distinct() : 스트림의 중복 요소를 제거

 

Mapping

  • map() : map에 명령문을 인수로 전달하여, 반환값들로 구성된 새로운 스트림을 반환
  • flatMap() : 스트림 요소가 배열인 경우, 주어진 명령문을 적용한 새로운 스트림으로 반환

 

import java.util.stream.*;

public class IntermediateOperationExample {
	public static void main(String[] args) {
    	String[] names = {"a", "b", "ab", "ab", "c"};
        System.out.println("Filtering");
        Stream<String> filterStream = Stream.of(names);
        filterStream.filter(name -> name.contains("a")).forEach(System.out::println);
        
        System.out.println("Distinct");
        Stream<String> distinctStream = Stream.of(names);
        distinctStream.distinct().forEach(System.out::println);
        
        System.out.println("Map");
        IntStream mapStream = IntStream.of(7, 3, 5, 4, 6);
        mapStream.map(num -> num + 2).forEach(System.out::println);
        
        System.out.println("flatMap");
        String[] wordArray = {"this is", "Intermediate Operation", "java jdk1.8"};
        Stream<String> flatMapStream = Stream.of(wordArray);
        flatMapStream.flatMap(s -> Stream.of(s.split(" "))).forEach(System.out::println);
    }
}

 

Sorting

  • sorted() : Comparator를 구현하여 넘겨주거나 natural order로 정렬

 

Splitting

  • limit() : 스트림에서 limit에 들어가는 수만큼의 새로운 스트림을 반환
  • skip() : 스트림의 첫 요소부터 skip에 들어가는 수를 제외한 새로운 스트림을 반환

 

Iterating

  • peek() : 각 요소를 돌면서 peek에 전달된 명령문을 수행

 

import java.util.stream.*;

public class IntermediateOperationExample {
	public static void main(String[] args) {
        System.out.println("Sorted");
        IntStream unsortedStream = IntStream.of(7, 3, 5, 4, 6);
        unsortedStream.sorted().forEach(System.out::println);
        
        System.out.println("limit");
        IntStream limitStream = IntStream.of(7, 3, 5, 4, 6);
        limitStream.limit(3).forEach(System.out::println);
        
        System.out.println("skip");
        IntStream skipStream = IntStream.of(7, 3, 5, 4, 6);
        skipStream.skip(3).forEach(System.out::println);
        
        System.out.println("peek");
        IntStream peekStream = IntStream.of(7, 3, 5, 4, 6);
        peekStream.sorted().peek(i -> System.out.println("sorted 수행 : " + i)
        		.forEach(System.out::println);
    }
}

 

 

스트림 최종 연산

앞에서 수행된 중개 스트림을 전달받아 최종적인 연산을 진행하여 이저의 스트림을 사용할 수 없게 됨

  • Calculating
  • Collecting
  • Reducing
  • Matching
  • Iterating

 

Calculating

  • count(), min(), max() : 요소의 개수와 최댓값, 최솟값을 반환
  • sum(), average() : 요소들의 합과 평균 값을 반환

 

Collecting

  • collect() : 매개변수로 Collectors의 구현 메소드를 받아 메소드의 동작대로 요소를 수집하여 반환

 

Reducing

  • reduce() : 스트림의 첫 두 요소를 가지고 여산 후, 이후 요소들로 연산한 값을 반환

 

import java.util.*;
import java.util.stream.*;

public class TerminateOperationExample {
	public static void main(String[] args) {
    	System.out.println("count");
        IntStream countStream = IntStream.of(7, 3, 5, 4, 6);
        System.out.println(countStream.skip(2).count());
        
        System.out.println("average");
        IntStream averageStream = IntStream.of(7, 3, 5, 4, 6);
        OptionalDouble averageResult = averageStream.skip(2).average();
        System.out.println(averageResult.getAsDouble());
        
        System.out.println("collect");
        IntStream collectStream = IntStream.of(7, 3, 5, 4, 6);
        List<Integer> collectedList = collectStream.sorted().boxed()
        	.collect(Collectors.toList());
        for (int element : collectedList) {
        	System.out.println(element);
        }
        
        System.out.println("reduce");
        IntStream reduceStream = IntStream.of(7, 3, 5, 4, 6);
        Optional<Integer> reduceResult = reduceStream.sorted().boxed()
        	.reduce((num1, num2) -> num1 * num2);
        System.out.println(reduceResult.get());
    }
}

 

 

Matching

  • anyMatch() : 스트림이 일부 요소가 특정 조건을 만족하면 true를 반환
  • allMatch() : 스트림이 모든 요소가 특정 조건을 만족하면 true를 반환
  • noneMatch() : 스트림이 모든 요소가 특정 조건을 만족하지 않으면 true를 반환
  • findFirst() : 스트림의 첫 요소를 반환
  • findAny() : 스트림의 첫 요소를 반환하는 것 같지만 parallelStream의 경우에 쓰임

 

Iterating

  • foreach() : 각 요소를 돌면서 foreach에 전달된 명령문을 수행

 

import java.util.*;
import java.util.stream.*;

public class TerminateOperationExample {
	public static void main(String[] args) {
    	System.out.println("anyMatch");
        IntStream anyMatchStream = IntStream.of(7, 3, 5, 4, 6);
        System.out.println(anyMatchStream.anyMatch(n -> n % 2 == 1));
        
        System.out.println("allMatch");
        IntStream allMatchStream = IntStream.of(7, 3, 5, 4, 6);
        System.out.println(allMatchStream.allMatch(n -> n / 2 == 1));
        
        System.out.println("findFirst");
        IntStream findFirstStream = IntStream.of(7, 3, 5, 4, 6);
        OptionalInt findFirstResult = findFirstStream.sorted().findFirst();
        System.out.println(findFirstResult.getAsInt());
        
        System.out.println("findAny");
        IntStream findAnyStream = IntStream.of(7, 3, 5, 4, 6);
        OptionalInt findAnyResult = findAnyStream.sorted().findAny();
        System.out.println(findAnyResult.getAsInt());
    }
}

 

 

Optional 클래스

Null값으로 인한 예외를 회피할 수 있는 래퍼 클래스

  • 모든 타입의 참조 변수를 저장 가능
  • 메소드를 이용하여 NullPointerException을 회피 가능
  • Stream과 비슷하게 기본 데이터혀을 처리할 수 있는 래퍼 클래스 지원
import java.util.*;

public class OptionalExample {
	public static void main(String[] args) {
    	Optional<String> optionalNullStr = Optional.ofNullable(null);
        Optional<String> optionalStr = Optional.ofNullable("null");
        Optional<Integer> optionalInt = Optional.of(0);
        OptionalDouble optionalDouble = OptionalDouble.of(3.14);
        
        // Optional 객체의 값이 null인지 여부 반환
        System.out.println(optionalNullStr.isPresent());
        // Optional 객체의 값이 null이면 매개변수 출력
        System.out.println(optionalNullStr.orElse("NULL값"));
        // Optional 객체의 값이 존재하면 매개변수로 전달된 명령문 수행
        optionalStr.isPresent(System.out::println);
        // Optional 값 출력
        System.out.println(optionalInt.get());
        // OptionalDouble 값 출력
        System.out.println(optionalDouble.getAsDouble());
    }
}

 

 

P. 특정 수를 입력 받고 해당 숫자 안의 모든 소수를 list로 만드는 스트림 작성

Condition

  • 반환된 스트림을 print문으로 출력
import java.util.*;

public class Problem {
	public static void main(String[] args) {
    	Scanner sc = new Scanner(System.in);
        int bound = sc.nextInt();
        List<Integer> result = calculatePrimeNumbers(bound);
        for (int i : result) {
        	System.out.println(i);
        }
    }
    
    public static List<Integer> calculatePrimeNumbers(int n) {
    	return IntStream.rangeClosed(2, n)
                .filter(x -> isPrimeNumber(x)).boxed()
                .collect(Collectors.toList());
    }
    
    private static boolean isPrimeNumber(int number) {
    	return Intstream.rangeClosed(2, (int)(Math.sqrt(number)))
                .allMatch(n -> number % n != 0);
    }
}