사이먼's 코딩노트
[Java] Stream 본문
[스트림(Stream)]
- 자바 스트림이란 Java 8부터 추가된 기술로 람다를 활용하여 배열과 컬렉션을 함수형으로 간단하게 처리할 수 있는 기술이다.
- Stream 이라는 단어의 뜻대로 데이터의 흐름을 의미한다.
- 기존의 for문이나 배열을 사용하면 코드가 길어져서 가독성과 재사용성이 떨이지며, 데이터 타입마다 다른 방식으로 다뤄야하는 불편함이 있다.
- 하지만, 스트림은 데이터 소스를 추상화하고, 데이터를 다루는 데에 자주 사용되는 메서드를 정의해 놓아서 데이터 소스에 상관없이 모두 같은 방식으로 다룰 수 있으므로 코드의 재사용성이 높아진다.
- 스트림을 사용하면 파일에 저장된 데이터도 모두 같은 방식으로 다룰 수 있다.
- 스트림은 일회용이라는 특징을 가지고있다.
[반복문]
- 아래 코드는 일반 for문 버전의 반복문과, Stream을 사용한 반복문을 작성한 코드이다.
package org.example;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
// 일반 버전
for(int i = 1; i <= 10; i++) {
System.out.println(i);
}
// 스트림 버전
IntStream.range(1, 11)
.forEach(e -> {
System.out.println(e);
});
IntStream.rangeClosed(1, 10)
.forEach(e -> { System.out.println(e); });
IntStream.rangeClosed(1, 10)
.forEach(System.out::println);
}
}
- 보통 for문을 사용할 때는 i의 시작과 끝의 값, 보폭을 선언하고 for문 안에 반복하고자 하는 코드를 작성한다.
- Stream을 이용하여 반복문을 작성할 때는 위의 예시와 같이 3가지 버전으로 코드를 작성할 수 있다.
- 3가지 버전은 모두 같은 결과를 보여주지만, 아래로 내려갈수록 코드가 간소화된 모습을 볼 수 있다.
- 먼저 시작과 끝의 값을 지정해주기 위해서 IntStream.range 또는 rangeClosed를 사용하는데 range 뒤에 오는 인자와 rangeClosed 뒤에 오는 인자의 값에는 차이가 있다.
- 인자에는 범위가 오게 되는데 range(1, 11)은 1부터 10까지 끝 값인 11를 포함하지 않고, rangeClosed(1, 10)도 1부터 10까지 끝 값인 10을 포함한다.
- forEach()를 통해 for문과 같은 역할을 수항하고, 매개변수에 대입된 람다식을 데이터 소스의 모든 요소에 적용한다.
[배열]
- 다음은 일반 배열을 선언하여 각 배열의 자리에 있는 숫자에 2를 곱한 결과와 Stream을 사용하여 각 배열의 자리에 있는 숫자에 2를 곱한 결과를 작성한 코드이다.
package org.example;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
// 일반 배열
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for(int i = 0; i < arr.length; i++) {
arr[i] *= 2;
}
System.out.println(Arrays.toString(arr));
// 스트림 배열
arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] resultArr = Arrays.stream(arr)
.map(e -> {
return e * 2;
})
.toArray();
resultArr = Arrays.stream(arr)
.map(e -> e * 2)
.toArray();
System.out.println(Arrays.toString(resultArr));
}
}
- 보통 배열에 값을 저장하기 위해서는 { } 안의 값을 작성하고, 해당 값에 연산을 원하면 for문을 사용하여 arr[i]에 차례로 접근하여 연산을 진행한다.
- Stream을 이용하여 배열 안의 값을 연산하고 싶다면 위의 예시와 같이 2가지 버전으로 코드를 작성할 수 있다.
- 2가지 버전은 모두 같은 결과를 보여주지만, 아래로 내려갈수록 코드가 간소화된 모습을 볼 수 있다.
- Arrays.stream()을 통해 최초에 선언된 배열을 선택하고 .map()을 통해 원하는 연산을 진행하고 마지막에 .toArray()를 통해 다시 배열로 정렬한다.
- 출력문에서 단순히 resultArr를 출력한다면 해당 배열의 주소값이 나오기 때문에 배열 안에 위치한 값을 보기 위해선 Arrays.toString()을 반드시 작성해야한다.
[리스트]
- 다음은 일반 배열을 선언하여 각 배열의 자리에 있는 숫자가 홀수인지 짝수인지 먼저 필터링하여 리스트에 담아두고, 리스트에 있는 필터링된 값들에 2를 곱한 결과와 Stream을 사용하여 같은 방법을 적용한 코드이다.
package org.example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
// 일반 리스트 필터링
int[] arr = {12, 57, 64, 43, 55, 76, 24, 97, 82, 72};
// filter 시작
List<Integer> resultAl = new ArrayList<>();
for(int n : arr) if(n % 2 == 0) resultAl.add(n);
// filter 끝
// map 시작
for(int i = 0; i < resultAl.size(); i++) {
int newValue = resultAl.get(i) * 2;
resultAl.set(i, newValue);
}
// map 끝
System.out.println(resultAl);
// 스트림 리스트 필터링
arr = new int[]{12, 57, 64, 43, 55, 76, 24, 97, 82, 72};
resultAl = Arrays.stream(arr)
.filter(e -> e % 2 == 0)
.map(e -> e * 2)
.boxed()
.collect(Collectors.toList());
System.out.println(resultAl);
}
}
- arr에 담긴 값들을 필터링하기 위해 생성한 resultAl 리스트가 있다.
- 향상된 for문을 사용하여 짝수인 값들은 모두 add()를 통해 resultAl에 담아 필터링을 끝낸다.
- 그 다음, 필터링된 resultAl의 크기만큼 반복하면서 newValue 변수에 각 필터링된 숫자에 2를 곱하여 set()를 통해 다시 2를 곱한 newValue 값을 저장한다.
- Stream을 이용하여 위와 같은 방법을 동일하게 적용할 수 있다.
- Arrays.stream()을 통해 최초에 선언된 배열을 선택하고 .filter()를 통해 짝수를 필터링해준다.
- .map()을 통해 원하는 연산을 진행하고 .collect(Collectors.toList())를 통해 스트림의 모든 요소를 배열로 변환한다.
- 이 때, IntStream 같이 원시 타입에 대한 스트림 지원을 클래스 타입으로 전환하여 전용으로 실행 가능한 기능을 수행하기 위해서 .boxed()를 추가 작성한다.
반응형
'Java > Java' 카테고리의 다른 글
[Java] 예외처리 / 접근제한자 (0) | 2024.03.19 |
---|---|
[Java] 인터페이스 (2) | 2024.03.19 |
[Java] 제네릭 / HashMap / 정리 (0) | 2024.03.18 |
[Java] ArrayList (2) | 2024.03.18 |
[Java] 배열 / toString / equals / StringBuilder (2) | 2024.03.14 |