오늘은 토이 프로젝트 생성하면서 테이블 생성 테스트 코드 작성할 때
활용했던 java.util.stream.IntStream에 대해 포스팅해 보겠습니다😄
주요 메서드 정리 및 개념에 대해 다루고
실전 예제 코드를 통해서 각 메서드에 장단점을 알아보는 시간을 가지겠습니다 ❗
IntStream - 소개
IntStream은 Java 8에서 도입된 Stream API의 일부분으로,
'int' 기본형에 특화된 스트림입니다.
Stream API는 Java에서 함수형 프로그래밍 스타일을 사용하여 데이터 처리를 할 수 있도록 도와주고
IntStream은 'Stream<Integer>' 와는 다르게 오토박싱/언박싱의 오버헤드가 없기 때문에 성능적으로 유리합니다.
IntStream은 여러 가지 방법으로 다음과 같이 생성해서 사용할 수 있습니다.
IntStream.of(int... values)
IntStream.range(int startInclusive, int endExclusive)
IntStream.generate(IntSupplier)
IntStream.iterate(int seed, IntUnaryOperator f)
사용법을 확인해 보면
코드를 간결하게 사용할 수 있고, 간결함은 즉 가독성도 향상할 수 있습니다.
또한 앞서 언급했듯 'Stream<Integer>' 와는 다르게 오토박싱/언박싱이 필요 없기 때문에 성능을 향상할 수 있고
람다 표현식과 함께 사용하여 함수형 프로그래밍을 구현할 수 있습니다.
(람다식은 JDK 1.8(Java 8 버전) 이후에 추가된 객체지향언어입니다. 알아보시는 분들은 아래 블로그 참고해 보시면 좋을 거 같습니다.)
물론 장점이 있다면 단점도 있습니다.
스트림은 한 번만 사용할 수 있어서 다시 사용하려면 새로 생성해야 하는 1회성이고,
성능에 민감한 낮은 수준의 제어가 필요한 경우 부적합할 수 있습니다.
또한, 스트림 파이프라인이 복잡해지면 디버깅이 어려울 수 있다는 단점이 있는데,
그렇다면 어떤 상황에 사용해야 적합하고 사용하면 안 되는 상황을 나열해 보겠습니다.
사용에 적합한 상황
- 대량의 데이터를 변환하거나 필터링하는 상황
- 합계, 평균, 최솟값, 최댓값 등 집계 작업이 필요한 상황
- 간결하고 가독성 좋은 코드가 필요한 상황
- 데이터 병렬 처리로 성능 향상이 필요한 상황
사용하면 안 되는 상황
- 복잡한 상태 변환이 필요할 때는 부적합합니다.
- 스트림은 무상태(stateless)를 권장합니다.
- 성능에 민감하고 낮은 수준의 제어가 필요할 때 부적합합니다.
- 예를 들어 루프를 정교하게 제어해야 하는 경우에는 부적합합니다.
- 디버깅이 중요한 코드에는 부적합합니다.
- 스트림 파이프라인이 복잡하면 디버깅이 어려울 수 있습니다.
IntStream - 사용법 및 예제
IntStream을 사용법으로 기본적으로 사용하는 예제와 평균 계산, 병렬 스트림 사용할 때 사용법으로 다뤄보겠습니다.
먼저 기본적으로 사용하는 예제입니다.
기본적인 사용 예제
IntStream을 생성하고 1~10까지의 수 중에 짝수 값을 출력하는 코드입니다.
import java.util.stream.IntStream;
public class TestIntStream {
public static void main(String[] args) {
// IntStream 생성 및 기본 연산
IntStream.range(1, 10)
.filter(n -> n % 2 == 0)
.forEach(System.out::println);
}
}
위 코드를 분석해 보면 range(1,10)에 1부터 9까지의 값을 생성하고 해당 필터를 통해서 짝수만 필터링합니다.
출력해 보면 다음과 같은 결과를 확인할 수 있습니다.
2
4
6
8
평균 계산 예제
평균 계산 예제로는 1부터 99가지의 값을 생성해서 짝수 값들의 평균을 계산하는 코드입니다.
import java.util.OptionalDouble;
import java.util.stream.IntStream;
public class TestIntStream {
public static void main(String[] args) {
// IntStream을 사용하여 평균 계산
OptionalDouble average = IntStream.range(1, 100)
.filter(n -> n % 2 == 0)
.average();
average.ifPresent(System.out::println);
}
}
average()는 필터링된 값들의 평균을 계산할 때 사용합니다.
ifPresent()은 평균이 존재하면 출력할 수 있게 선언하였고 실행해 보면 다음과 같은 평균값을 확인할 수 있습니다.
50.0
병렬 스트림 예제
import java.util.stream.IntStream;
public class TestIntStream {
public static void main(String[] args) {
// 병렬 스트림을 사용하여 합계 계산
int sum = IntStream.range(1, 1000000)
.parallel()
.sum();
System.out.println("Sum: " + sum);
}
}
병렬 스트림 예제에서는 1부터 999999까지의 값을 생성하고
'parallel()'을 선언하여 스트림을 병렬 처리와 sum() 메서드를 사용해서 모든 값을 더합니다.
출력 결과는 다음과 같습니다.
Sum: 499999500000
지금까지 각 예제를 통해서 IntStream의 사용법에 대해 알아봤습니다.
마지막으로 주요 메서드를 예제를 통해서 알아보고 마무리하겠습니다.
IntStream - 주요 메서드 소개 및 예제
주요 메서드를 예제를 통해서 알아볼 텐데,
별도의 설명은 코드에 간단하게 주석 처리하겠습니다.
Map
import java.util.stream.IntStream;
public class TestIntStream {
public static void main(String[] args) {
// IntStream을 사용하여 값을 제곱
// map => 각 요소에 함수(여기서는 제곱)를 적용하여 새로운 스트림을 반환
IntStream.range(1, 5)
.map(n -> n * n)
.forEach(System.out::println);
}
}
// 출력 결과
1
4
9
16
reduce
import java.util.stream.IntStream;
public class IntStreamExample {
public static void main(String[] args) {
// IntStream을 사용하여 값의 합을 계산
// reduce => 초기값(0)과 누적기 함수((a,b) -> a+b)를 사용하여 스트림의 모든 요소를 결합
int sum = IntStream.range(1, 5)
.reduce(0, (a, b) -> a + b);
System.out.println("Sum: " + sum);
}
}
//출력 결과
Sum: 10
sum
import java.util.stream.IntStream;
public class IntStreamExample {
public static void main(String[] args) {
// IntStream을 사용하여 값의 합을 계산
// sum => 모든 요소를 더한다
int sum = IntStream.range(1, 5).sum();
System.out.println("Sum: " + sum);
}
}
//출력 결과
Sum: 10
max
import java.util.OptionalInt;
import java.util.stream.IntStream;
public class IntStreamExample {
public static void main(String[] args) {
// IntStream을 사용하여 최대값을 찾기
// max => 최대값을 반환
OptionalInt max = IntStream.range(1, 5).max();
max.ifPresent(value -> System.out.println("Max: " + value));
}
}
//출력 결과
Max: 4
min
import java.util.OptionalInt;
import java.util.stream.IntStream;
public class IntStreamExample {
public static void main(String[] args) {
// IntStream을 사용하여 최소값을 찾기
// min => 최소값을 반환
OptionalInt min = IntStream.range(1, 5).min();
min.ifPresent(value -> System.out.println("Min: " + value));
}
}
//출력 결과
Min: 1
average
import java.util.OptionalDouble;
import java.util.stream.IntStream;
public class IntStreamExample {
public static void main(String[] args) {
// IntStream을 사용하여 평균을 계산
// average => 스트림의 평균값을 반환
OptionalDouble avg = IntStream.range(1, 5).average();
avg.ifPresent(value -> System.out.println("Average: " + value));
}
}
//출력 결과
Average: 2.5
distinct
import java.util.stream.IntStream;
public class TestIntStream {
public static void main(String[] args) {
// IntStream을 사용하여 중복 제거
// distinct => 중복된 요소를 제거하고 새로운 스트림을 반환
IntStream.of(1, 2, 2, 3, 4, 4, 5)
.distinct()
.forEach(System.out::println);
}
}
//출력 결과
1
2
3
4
5
sorted
import java.util.stream.IntStream;
public class TestIntStream {
public static void main(String[] args) {
// IntStream을 사용하여 정렬
// sorted => 스트림의 요소를 정렬
IntStream.of(5, 3, 1, 4, 2)
.sorted()
.forEach(System.out::println);
}
}
//출력 결과
1
2
3
4
5
boxed
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class TestIntStream {
public static void main(String[] args) {
// IntStream을 사용하여 Integer 객체 리스트로 변환
List<Integer> integerList = IntStream.range(1, 5)
.boxed()
.collect(Collectors.toList());
System.out.println(integerList);
}
}
//출력 결과
[1, 2, 3, 4]
마치며
'IntStream'을 통해 성능을 개선하고, 간결한 코드를 작성할 수 있지만
모든 상황에 적합하지 않으므로 적절한 상황에서 사용하는 것이 중요한 거 같습니다❗
특히, 성능에 민감한 낮은 수준의 제어나 복잡한 상태 변환이 필요할 때는 다른 방법을 고려해 봐야겠습니다😅
'[ JAVA ] > JAVA' 카테고리의 다른 글
[ Java ] 동시성 제어를 위한 세 가지 키워드 / CAS 알고리즘 개념 알아가기 (0) | 2024.08.21 |
---|---|
[ Java ] Optional 개념 및 올바른 사용법 알아가기 (1) | 2024.06.10 |
[ JAVA ] Iterator 개념 및 예제 (0) | 2023.07.21 |
[ Java ] Lambda (0) | 2023.01.17 |
[ Java ] File 클래스 (0) | 2023.01.16 |