Stream이란?
Java8에서부터 등장한 Stream은 컬렉션 혹은 배열의 요소들에 대한 반복 처리를 효율적으로 처리할 수 있게 하는 기능이다. 과거에는 for나 for each 등을 통해 순차적으로 요소에 접근하며 작업하였기 때문에 코드가 다소 복잡해지거나, 성능이 좋지 못했다. 하지만 Stream이 등장하면서 이를 선언형으로 보다 가독성이 좋으면서도 작성하기도 쉬운 형태로 이를 처리할 수 있게 되었다.
Stream의 특징
1. 선언적인 코드 작성 방식
여러 함수형 인터페이스들을 이용하면서 ‘무엇을(what)’ 하는지를 선언하고, 그 내부적인 동작에 대해서는 숨기는 방식으로 코드를 적는다. 아래 코드에서 for문은 어떻게 데이터를 처리할 것인지에 대해서 내부 동작을 하나하나 설정해주었다면, stream을 사용한 코드에서는 각 요소를 두 배로 만들어라는 의도 선언만 할뿐, 내부적인 반복이나 조건 체크 등에서는 얘기하지 않는다. (즉, 반복문이 코드로 보이지 않는다는 것이고 이는 곧 간결하고 가독성 좋은 코드로 반복을 처리할 수 있다는 의미이기도 하다. 처리해야 하는 연산이 복잡해질 수록 더 큰 장점이 된다.)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> doubled = new ArrayList<>();
for (int number : numbers) {
doubled.add(number * 2);
}
List<Integer> doubled = numbers.stream()
.map(number -> number * 2)
.collect(Collectors.toList());
2. Stream은 일회용이며 원본 데이터를 바꾸지 않는다.
stream은 stream을 만드는 원본 데이터를 건드리지 않고 복사하여 사용한다. 즉 원본 데이터의 보존을 보장하면서도 그 데이터를 연산하여 새로운 데이터를 만들어 다른 곳에서 사용할 수 있다. 하지만 이때 stream은 한 번 사용하여 닫히면 재사용이 불가하기 때문에, stream을 통해 만들어진 데이터를 재사용하고 싶다면 별도의 자료형에 담아 사용해야 한다.
Stream의 사용방법
Stream은 아래 3단계를 따라 사용할 수 있다. 각 단계들을 도트(dot)을 통해 이어 작성하는 방식으로 진행된다.
1. Stream 생성: Stream을 생성하고자 하는 변수에 대해서 stream을 생성한다. 이때 stream 생성 가능한 것들은 연속된 데이터를 가지고 있는 것들이 보통이며(컬렉션, 배열, 파일 등) 특이하게 빈 스트림을 생성할 수도 있다.
2. 중간 연산: 중간 연산은 stream을 받아 stream을 반환하는 연산이다(따라서 중개 연산을 연속으로 몇 번이고 사용할 수도 있다).
- Stream 필터링 : filter(), distinct()
- Stream 변환 : map(), flatMap()
- Stream 제한 : limit(), skip()
- Stream 정렬 ; sorted()
- Stream 연산 결과 확인 : peek()
3. 최종 연산: 중간 연산을 통해 만들어진 stream을 받아 stream이 아닌 값을 반환하여 최종적으로 마무리짓는 연산이다. 위에서 stream의 특성에서 얘기했듯, stream은 종료되면 재사용이 불가하기 때문에 최종 연산 뒤에는 모든 stream은 종료가 되고 없어지게 된다.
- 출력 : forEach()
- 소모 : reduce()
- 검색 : findFirst(), findAny()
- 매칭 : anyMatch(), allMatch(), noneMatch()
- 값 계산(통계적 연산) : count(), min(), max()
- 값 계산 : sum(), average()
- 수집 : collect()
예시
아래는 과일 이름들을 가지고 있는 List에 대해서 stream 연산을 수행하는 예시다. 각각 필터링, 반복, 집계하는 연산이며, 코드에서 볼 수 있든 반복문을 사용하지 않고도 직관적인 코드로 반복 작업을 처리할 수 있음을 확인할 수 있다.
public class StreamExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "pear", "kiwi");
// 필터링과 매핑
List<String> filtered = items.stream()
.filter(s -> s.startsWith("a"))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(filtered); // [APPLE]
// 반복
items.stream()
.forEach(System.out::println);
// 집계
long count = items.stream()
.filter(s -> s.contains("a"))
.count();
System.out.println("Count: " + count); // 3
}
}
'부트캠프' 카테고리의 다른 글
자바 기초 스터디 3주차 - Enum 사용하기 (0) | 2023.11.09 |
---|---|
자바 기초 스터디 2주차 - Getter와 Setter (1) | 2023.11.02 |
자바 기초 스터디 1주차 - Array vs ArrayList (1) | 2023.10.25 |