JaeWon's Devlog
article thumbnail
Published 2022. 8. 13. 12:45
[Java] Stream(스트림) 정리 BackEnd/Java
반응형

이전에 람다(Lambda)를 정리하면서 Java 1.8 부터 제공하는 람다를 활용하는 스트림(Stream) API에 대해서도 정리하고자 합니다.

기존의 Java 에서는 Collection 데이터(List, Map 등)를 처리할 때 for, while 문 등 특정 조건에 따라 요소 하나씩을 꺼내어 처리하면서 복잡한 과정을 거쳐야 했습니다.

아직도 람다 방식이 익숙치 않고 스트림 API도 종종 사용하지만 올바르게 활용하지는 못하고 있는 것 같아 정리하여 기록해보고자 합니다.


1. Stream 이란?

- 데이터를 추상화하고, 처리하는데 자주 사용되는 함수들을 정의한다.

데이터 추상화 : 데이터의 종류에 상관 없이 같은 방식으로 데이터를 처리

- 데이터의 흐름 -> 배열 또는 컬렉션에 함수 여러 개를 조합해서 원하는 결과를 필터링하고 가공된 결과를 얻을 수 있다.

- 스트림 API 는 다음과 같이 3단계에 걸쳐서 동작한다.

  1. 스트림의 생성
  2. 스트림의 중간 연산(스트림의 변환)
  3. 스트림의 최종 연산(스트림의 사용)

2. Stream 특징

  • 원본의 데이터를 변경하지 않음.
  • 일회용.
  • 내부 반복으로 작업을 처리.
  • 지연 연산.(최종 연산이 수행되기 전까지 중간 연산이 수행되지 않음)

2-1. 원본의 데이터를 변경하지 않음

- 원본의 데이터를 조회하여 원본의 데이터가 아닌 별도의 요소들로 Stream 을 생성한다.

- 원본의 데이터를 읽기만 할 뿐, 정렬이나 필터링 등의 작업은 별도의 Stream 요소들에서 처리한다.

List<Object> streamTest = testList.stream().sorted().collect(collectors.toList());

2-2. 일회용

- 한번 사용이 끝나면 재사용이 불가능하다.

- 필요시 다시 Stream 을 생성하여 사용해야 한다.(iterator 와 동일)

String[] arr = {"A", "C", "B", "E", "D"};

//	Stream(스트림) 생성
Stream<String> test = Arrays.asList(arr).stream();

//	Stream 사용
test.sorted().forEach(System.out::println);

//	실행시 에러 발생
//	java.lang.IllegalStateException: stream has already been operated upon or closed
System.out.println(test.count());

2-3. 내부 반복으로 작업 처리

- 기존 반복문을 사용하기 위해서는 for, while 등과 같은 문법을 사용했지만, 스트림에서는 메소드 내부에 숨기고 있어 간결한 코드 작성이 가능하다.

String[] arr = {"A", "C", "B", "E", "D"};
List<String> test = Arrays.asList(arr);

//	Stream 사용
test.stream()
    .forEach(System.out::println);
    
//	Stream 사용 X
for(int i = 0; i < test.legnth; i ++){
	System.out.println(test.get(i));
}

3. Stream API 연산 종류

3-1. 생성하기

- 스트림 객체를 생성하는 단계이다.

- 스트림은 재사용이 불가능하므로, 닫히면 재생성해야 한다.

3-2. 가공하기(중간연산)

- 원본의 데이터를 별도의 데이터로 가공하기 위한 중간 연산이다.

- 연산 결과를 스트림으로 다시 반환하기 때문에 연속해서 중간 연산이 가능하다.

distinct() 중복 요소 제거
sorted() 기존 요소 정렬
filter() 조건에 충족하는 데이터만을 정제하여 생성
peek() Stream 에 영향을 주지 않고 특정 연산을 수행
map() 기존 요소들을 변환하여 새로운 요소로 생성
mapToInt
mapToObj
일반적인 Stream 객체를 원시 Stream 으로 바꾸거나, 반대로 하는 작업

3-3. 결과만들기(최종연산)

- 가공된 데이터로부터 원하는 데이터를 만들기 위한 최종 연산이다.

max(), sum(), count() ... 요소들을 대상으로 최댓값, 총합, 갯수 등을 반환
collect() 요소들을 List 나 Set, Map 등 다른 종류의 결과로 반환
anyMatch(), allMatch() ... 요소들을 특정한 조건을 충족하는지 검사
forEach() 요소들을 대상으로 특정한 연산을 수행

4. Example

String[] arr = {"A", "C", "B", "E", "D", "1f", "1g"};

Arrays.asList(arr)
    .stream()					//	생성하기(Stream 생성하기)
    .filter(f -> f.startsWith("1"))		//	가공하기(중간연산, filter -> 1f, 1g 를 걸러냄)
    .map(m -> m.substring(1,2).toUpperCase())	//	가공하기(중간연산, map -> 1을 자르고 대문자로 변환하여 저장)
    .sorted()					//	가공하기(중간연산, sorted -> 정렬)
    .collect(Collectors.toList())		//	가공하기(중간연산, collect -> List 로 변환)
    .forEach(System.out::println);		//	결과만들기(최종연산, 반복하여 출력)
    
//	결과 : F G

참고

- https://mangkyu.tistory.com/112

- https://hipopatamus.tistory.com/m/66

반응형

'BackEnd > Java' 카테고리의 다른 글

[Java] 어노테이션(Annotation) 정리  (0) 2022.09.05
[Java] @SneakyThrows 란?(Lombok 어노테이션)  (2) 2022.08.20
[Java] MDC 를 사용한 로그(Log)추적하기  (0) 2022.07.30
[Java] Lambda 정리  (0) 2022.07.16
[Java] Lombok 정리  (0) 2022.07.06
profile

JaeWon's Devlog

@Wonol

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!