본문 바로가기

개발언어/Java _ Spring

[Java] Stream()의 기초 문법 및 예시코드

 

0. 개요

Java 8 이상에서 추가된 Stream API는 컬렉션, 배열 등의 데이터 소스를 다루기 위한 함수형 프로그래밍 방식을 제공한다.

Stream은 함수형 인터페이스를 사용하여 구현되며, 여러 연산들을 지원하여 데이터를 처리하고 결과를 반환한다.

 

스트림을 사용하면 기능을 구현하지 않고, 선언형으로 컬렉션형의 데이터들을 처리할 수 있다.

이 말은 다시 말해, 스트림을 사용하면 반복문이나 조건문을 하나의 문장에 간략히 작성할 수 있다.

 

예를 들어, 

List<String> result = new ArrayList<>();
        
        for (String userNumber : userNumbers) {
            if (userNumber.equals(targetNumber)) {
                result.add(userNumber);
            }
        }
        
        return result;

result라는 List에 userNumber가 targetNumber와 일치할 때, 값을 넣고자 하는 조건문이 포함된 반복문이 있다고 할 때

 

  // 가독성을 위해 메서드 마다 라인을 바꿔주는 것이 좋다. 
userNumbers.stream()
           .filter(userNumber -> userNumber.equals(targetNumber))
           .collect(Collectors.toList());

위와 같은 형태로 한 줄로 표현이 가능해진다. 구조와 각 메서드에 대한 설명을 이어가면

 

1. 문법 구조

생성하기 - 가공하기 - 결과 만들기 
List.stream() 					// stream생성하기
	.map(a -> a.toUpperCase())			// 가공하기_매핑
    .forEach(System.out.println(System.out::println)); // 결과만들기

1_1) 생성하기 _ 컬렉션으로부터 스트림의 구조를 생성하기

1_2) 가공하기 _ 중간연산을 통해 찾고자 하는 데이터를 결과 만들기의 형태에 적합하게 가공한다. 이 단계는 어러번 사용될 수 있다.

1_3) 결과만들기 _ 스트림을 원하는 컬렉션 자료구조로 변환하여 반환한다

 

 

예시코드와 출력되는 값들을 같이 살펴보자

 


2. 예시코드

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "apple" "orange", "kiwi", "watermelon");

        // filter()
        fruits.stream()
              .filter(fruit -> fruit.startsWith("a"))
              .forEach(System.out::println);
        // 출력: apple apple

        // map()
        fruits.stream()
              .map(fruit -> fruit.toUpperCase())
              .forEach(System.out::println);
        // 출력: APPLE BANANA APPLE ORANGE KIWI WATERMELON

        // sorted()
        fruits.stream()
              .sorted()
              .forEach(System.out::println);
        // 출력: apple apple banana kiwi orange watermelon

        // reduce()
        String result = fruits.stream()
                              .reduce("", (s1, s2) -> s1 + s2);
        System.out.println(result);
        // 출력: applebananaappleorangekiwiwatermelon
        
        // distinct()
        fruits.stream()
              .distinct()
              .forEach(System.out::println);
        // 출력: apple banana orange kiwi watermelon

        // limit()
        fruits.stream()
              .limit(3)
              .forEach(System.out::println);
        // 출력: apple banana apple

        // skip()
        fruits.stream()
              .skip(2)
              .forEach(System.out::println);
        // 출력: apple orange kiwi watermelon

        // count()
        long count = fruits.stream()
                           .count();
        System.out.println(count);
        // 출력: 6
    }
}

 

_1) 예시코드에서 보면 알수 있듯, 컬렉션 (List)을 Stream 형태로 변경하고

_2) 결과값을 뽑기에 적당한 형태로 가공하고

_3) 결과값을 반환한다 (예시코드에서는 모두 출력문이다)

 

Stream(중간연산)에서 자주 사용되는 가공 메서드
  • filter(): 필터링_ 특정 조건에 부합하는 stream을 전개한다
  • map(): 매핑처리_ 리스트 내의 모든 값들을 키_값형태로 처리한다
  • sorted(): 알파벳순으로 정렬한다
  • reduce(): 모든 과일을 하나의 문자열로 연결한다.
  • distinct(): 중복된 값을 제거하고 출력한다.
  • limit(long maxSize): 처음부터 maxSize개의 값을 출력한다.
  • skip(long n): 처음 n개의 값을 제외한 나머지 값을 출력한다.

Stream(최종연산)에서 자주 사용되는 메서드
  • forEach(Consumer<T> action): 각 요소에 대해 주어진 action을 실행한다
  • count(): 스트림의 요소 수를 반환
  • collect(Collector<T, A, R> collector): 스트림의 요소를 수집하여 Collection, List, Set, Map 등의 컬렉션에 저장하는 최종 연산
  • toArray(): 스트림의 요소를 배열로 반환하는 최종 연산
  • reduce(T identity, BinaryOperator<T> accumulator): 스트림의 요소를 identity로 초기화한 후, accumulator 연산을 반복하여 값을 줄여나가는 최종 연산 예시코드에서는 더하기연산으로 문자열을 이어쓰는 형태로 활용하였다
  • min(Comparator<T> comparator): 스트림에서 최소값을 반환
  • max(Comparator<T> comparator): 스트림에서 최대값을 반환
  • anyMatch(Predicate<T> predicate): 스트림에서 predicate 조건을 만족하는 요소가 하나라도 있는지 확인
  • allMatch(Predicate<T> predicate): 스트림의 모든 요소가 predicate 조건을 만족하는지 확인

 


3. 활용 코드 

private Map<String, Object> getOrderInfo(List<OrderMngModel> orderItemList) {
    String XComOrderStatus = "OD0001"; // 구매요청

    boolean isPreOrder = orderItemList.stream()
                                      .filter((order) -> "XCOM".equals(order.getInpFgCd()))
                                      .anyMatch((order) -> "PRE_ORDER".equals(order.getOrderState()));
//    anyMatch() : 최소한 한 개의 요소가 주어진 조건에 만족하는지 조사 -> true / false 반환

if (isPreOrder) XComOrderStatus = "OD0001.5"; // 가주문
    


}

getOrderInfo는 주문상태를 결정하는 메서드로

초기값인

String XComOrderStatus = "OD0001"; // 구매요청

으로 선언 후,

주문 상태가 변경됨에 따라 값이 경신되는 요소들을 검사하여 해당 조건이 만족할 시, 주문상태가 바뀔 수 있게 처리하는 코드이다

 

해당 코드를 같은 의미의 for문으로 변경하여 확인해 보면 이해가 보다 쉬울 수 있다.  

boolean isisPreOrder = false;
for (OrderMngModel order : orderItemList){
    if("XCOM".equals(order.getInpFgCd()) && "PRE_ORDER".equals(order.getOrderState()) ){
        isPreOrder = true;
        break;
    }
}

if(isPreOrder) XComOrderStatus = "OD0001.5"; //OD0001 : 구매요청, OD0001.5 : 가주문