Stream API๋ Java 8์์ ๋์
๋ ๊ธฐ๋ฅ์ผ๋ก ์ปฌ๋ ์
๋ฐ์ดํฐ๋ฅผ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์คํ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ ๊ณต๋๋ API์ด๋ค. ๊ธฐ์กด์ for๋ฌธ์ด๋ Iterator๋ฅผ ์ด์ฉํ ์ธ๋ถ ๋ฐ๋ณต ๋ฐฉ์ ๋์ ๋ด๋ถ ๋ฐ๋ณต ๋ฐฉ์์ ์ฌ์ฉํ์ฌ ์ฝ๋์ ๊ฐ๋
์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํจ๋ค.
// for๋ฌธ
for (String name : names) {
if (name.length() > 4) {
System.out.println(name.toUpperCase());
}
}
// Stream
names.stream()
.filter(n -> n.length() > 4)
.map(String::toUpperCase)
.forEach(System.out::println);
Stream API๋ ์ฐ์๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๊ณผ์ ์ ํ์ดํ๋ผ์ธ ํํ๋ก ์ฐ๊ฒฐํ์ฌ ์ํํ๋ค. ์คํธ๋ฆผ ํ์ดํ๋ผ์ธ์ ์ผ๋ฐ์ ์ผ๋ก ์คํธ๋ฆผ ์์ฑ -> ์ค๊ฐ ์ฐ์ฐ -> ์ต์ข ์ฐ์ฐ์ ์ธ ๋จ๊ณ๋ก ๊ตฌ์ฑ๋๋ค.
์ปฌ๋ ์ , ๋ฐฐ์ด, ํ์ผ ๋ฑ์ ๋ฐ์ดํฐ ์์ค๋ก๋ถํฐ ์คํธ๋ฆผ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค. Java์์๋ ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก Stream์ ์์ฑํ ์ ์๋ค.
// 1. ์ปฌ๋ ์ ์์ ์์ฑ
List<String> list = List.of("Blue", "Cool", "Good");
Stream<String> stream1 = list.stream();
// 2. ๋ฐฐ์ด์์ ์์ฑ
String[] arr = {"This", "is", "Array"};
Stream<String> stream2 = Arrays.stream(arr);
// 3. ์ซ์ ๋ฒ์์์ ์์ฑ
IntStream intStream = IntStream.range(1, 5);
LongStream longStream = LongStream.rangeClosed(1, 5);
// 4. ์ง์ ์์ฑ
Stream<Double> randoms = Stream.generate(Math::random).limit(5);
// 5. ๋น๋ ์ฌ์ฉ
Stream<String> buildStream = Stream.<String>builder()
.add("This")
.add("is")
.add("Builder")
.build();
๋ฐ์ดํฐ๋ฅผ ๋ณํ, ํํฐ๋ง, ์ ๋ ฌํ๋ ๊ณผ์ ์ ๋ด๋นํ๋ฉฐ Stream ํ์ดํ๋ผ์ธ์ ์ค๊ฐ ๋จ๊ณ์์ ์ํ๋๋ ์ฐ์ฐ์ด๋ค. ์ด ๊ณผ์ ์์ ์ง์ฐ ์ฐ์ฐ(Lazy Evaluation)์ด ๋ฐ์ํ๋ค.
์ง์ฐ ์ฐ์ฐ์ด๋ ์ต์ข
์ฐ์ฐ์ด ํธ์ถ๋๊ธฐ ์ ๊น์ง ์ค์ ์ฐ์ฐ์ด ์ํ๋์ง ์๊ณ ์ต์ข
์ฐ์ฐ์ด ์คํ๋๋ ์์ ์ ์ ์ฒด ํ์ดํ๋ผ์ธ์ด ํ ๋ฒ์ ์ฒ๋ฆฌ๋๋ ๋ฐฉ์์ ์๋ฏธํ๋ค.
๋ํ ์ค๊ฐ ์ฐ์ฐ์ Stream ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์ฌ๋ฌ ์ฐ์ฐ์ ์ฐ๊ฒฐํ๋ ์ฒด์ด๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋ค. ์ด๋ฅผ ํตํด ์ฌ๋ฌ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๊ณผ์ ์ ํ๋์ ํ์ดํ๋ผ์ธ์ผ๋ก ๊ตฌ์ฑํ ์ ์๋ค.
๋ํ์ ์ธ ์ค๊ฐ ์ฐ์ฐ ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
filter(Predicate) // ์กฐ๊ฑด์ ๋ง๋ ์์๋ง ํํฐ๋ง
map(Function) // ์์๋ฅผ ๋ค๋ฅธ ํํ๋ก ๋ณํ
flatMap(Function) // ์ค์ฒฉ๋ ๊ตฌ์กฐ๋ฅผ ํํํ
sorted() / sorted(Comparator) // ์์ ์ ๋ ฌ
distinct() // ์ค๋ณต ์ ๊ฑฐ
limit(long) // ์์์๋ถํฐ n๊ฐ ์์ ์ ํ
skip(long) // ์์์๋ถํฐ n๊ฐ ์์ ๊ฑด๋๋
peek(Consumer) // ์์๋ฅผ ์๋นํ์ง ์๊ณ ์ค๊ฐ ์ฒ๋ฆฌ ๊ณผ์ ํ์ธ
์คํธ๋ฆผ ํ์ดํ๋ผ์ธ์ ๋ง์ง๋ง ๋จ๊ณ์์ ์ํ๋๋ ์ฐ์ฐ์ผ๋ก ์ค๊ฐ ์ฐ์ฐ์ ํตํด ์ฒ๋ฆฌ๋ ๋ฐ์ดํฐ๋ฅผ ์ต์ข
๊ฒฐ๊ณผ๋ก ๋ฐํํ๊ฑฐ๋ ์๋นํ๋ ์ญํ ์ ํ๋ค.
์ต์ข
์ฐ์ฐ์ด ํธ์ถ๋๋ฉด ๊ทธ๋๊น์ง ์ ์๋ ์ค๊ฐ ์ฐ์ฐ๋ค์ด ํ ๋ฒ์ ์คํ๋๋ฉด์ ์คํธ๋ฆผ ํ์ดํ๋ผ์ธ์ด ์ข
๋ฃ๋๋ค. ๋ํ ์ต์ข
์ฐ์ฐ์ด ์ํ๋ ์คํธ๋ฆผ์ ์ฌ์ฌ์ฉํ ์ ์๋ค.
๋ํ์ ์ธ ์ต์ข
์ฐ์ฐ ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
forEach(Consumer) // ๊ฐ ์์๋ฅผ ์ํํ๋ฉฐ ์ฒ๋ฆฌ
collect(Collector) // ์คํธ๋ฆผ ์์๋ฅผ ์ปฌ๋ ์ ๋ฑ์ผ๋ก ๋ณํ
toArray() // ์คํธ๋ฆผ ์์๋ฅผ ๋ฐฐ์ด๋ก ๋ณํ
reduce(BinaryOperator) // ์์๋ฅผ ๋์ ์ฐ์ฐํ์ฌ ํ๋์ ๊ฒฐ๊ณผ๋ก ๋ฐํ
count() // ์์์ ๊ฐ์ ๋ฐํ
findFirst() // ์ฒซ ๋ฒ์งธ ์์ ๋ฐํ
findAny() // ์์ ์ค ํ๋๋ฅผ ๋ฐํ (๋ณ๋ ฌ ์ฒ๋ฆฌ ์ ์ฃผ๋ก ์ฌ์ฉ)
allMatch(Predicate) // ๋ชจ๋ ์์๊ฐ ์กฐ๊ฑด์ ๋ง์กฑํ๋์ง ํ์ธ
anyMatch(Predicate) // ํ๋๋ผ๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋์ง ํ์ธ
noneMatch(Predicate) // ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์์๊ฐ ์๋์ง ํ์ธ
min(Comparator) // ์ต์๊ฐ ๋ฐํ
max(Comparator) // ์ต๋๊ฐ ๋ฐํ
์คํธ๋ฆผ ์์ฑ๋ถํฐ ์ฐ์ฐ๊น์ง์ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
List<String> names = List.of("BlueCool", "PYO", "MIN", "BlueMin");
// ๊ธธ์ด๊ฐ 4๋ณด๋ค ํฐ ์ด๋ฆ๋ง ํํฐ๋ง -> ๋๋ฌธ์๋ก ๋ณํ -> ์ํ๋ฒณ์ ์ ๋ ฌ -> ์ถ๋ ฅ
names.stream() // ์คํธ๋ฆผ ์์ฑ (Source)
.filter(name -> name.length() > 4) // ์ค๊ฐ ์ฐ์ฐ: ์กฐ๊ฑด ํํฐ๋ง (Predicate)
.map(String::toUpperCase) // ์ค๊ฐ ์ฐ์ฐ: ๋ฌธ์์ด์ ๋๋ฌธ์๋ก ๋ณํ (Function)
.sorted() // ์ค๊ฐ ์ฐ์ฐ: ์ํ๋ฒณ์ ์ ๋ ฌ
.forEach(System.out::println); // ์ต์ข ์ฐ์ฐ: ์์ ์ถ๋ ฅ (Consumer)
Stream API๋ ๋งค์ฐ ํธ๋ฆฌํ ๊ธฐ๋ฅ์ด์ง๋ง ๋ชจ๋ ์ํฉ์์ ํญ์ ์ต์ ์ ์ ํ์ ์๋๋ค. ๋ฐ์ดํฐ์ ํฌ๊ธฐ๊ฐ ์๊ฑฐ๋ ์ฐ์ฐ์ด ๋จ์ํ ๊ฒฝ์ฐ์๋ ๊ธฐ์กด์ ๋ฐ๋ณต๋ฌธ์ด ๋ ์ง๊ด์ ์ด๊ฑฐ๋ ์ฑ๋ฅ ๋ฉด์์ ์ ๋ฆฌํ ์๋ ์๋ค.
๋ํ ๊ธฐ๋ณธํ์ด ์๋ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋ฐ์ฑ(Boxing)๊ณผ ์ธ๋ฐ์ฑ(Unboxing) ๊ณผ์ ์์ ๋ถํ์ํ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ ์ ์๋ค. ์ด๋ฌํ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ค์ด๊ธฐ ์ํด Java์์๋ ๊ธฐ๋ณธํ ์ ์ฉ ์คํธ๋ฆผ์ ์ ๊ณตํ๋ค.
๋ํ์ ์ธ ๊ธฐ๋ณธํ ์คํธ๋ฆผ์ ๋ค์๊ณผ ๊ฐ๋ค.
IntStream LongStream DoubleStream ์ด๋ฌํ ๊ธฐ๋ณธํ ์คํธ๋ฆผ์ ์ฌ์ฉํ๋ฉด ๊ธฐ๋ณธ ํ์ ์ ์ง์ ์ฒ๋ฆฌํ ์ ์์ด ๋ถํ์ํ ๊ฐ์ฒด ์์ฑ๊ณผ ๋ฐ์ฑ/์ธ๋ฐ์ฑ ๋น์ฉ์ ์ค์ผ ์ ์๋ค.
// 1๋ถํฐ 9๊น์ง์ ์ซ์ ์ค ์ง์๋ง ์ ํํ ๋ค ํฉ๊ณ๋ฅผ ๊ตฌํ๋ ์์
IntStream.range(1, 10)
.filter(n -> n % 2 == 0)
.sum();
์ซ์ ์ฐ์ฐ์์ ๋ณด๋ค ํจ์จ์ ์ธ ์คํธ๋ฆผ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค.
Stream API๋ ๋ฐ์ดํฐ๋ฅผ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ์ด๋ฅผ ํตํด ์ฌ๋ฌ CPU ์ฝ์ด๋ฅผ ํ์ฉํ์ฌ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ๋์์ ์ํํ ์ ์๋ค.
๋ณ๋ ฌ ์คํธ๋ฆผ์ parallelStream() ๋๋ parallel() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑํ ์ ์๋ค.
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);
// ๋ณ๋ ฌ ์คํธ๋ฆผ ์์ฑ
numbers.parallelStream()
.map(n -> n * 2)
.forEach(System.out::println);
๋๋ ๊ธฐ์กด ์คํธ๋ฆผ์ parallel()์ ํธ์ถํ์ฌ ๋ณ๋ ฌ ์คํธ๋ฆผ์ผ๋ก ์ ํํ ์๋ ์๋ค.
numbers.stream()
.parallel()
.map(n -> n * 2)
.forEach(System.out::println);
โ๋ณ๋ ฌ ์คํธ๋ฆผ์ ๋ฐ์ดํฐ์ ํฌ๊ธฐ๊ฐ ํฌ๊ณ ์ฐ์ฐ๋์ด ๋ง์ ๊ฒฝ์ฐ ์ฑ๋ฅ ํฅ์์ ๋์์ด ๋ ์ ์๋ค. ํ์ง๋ง ์ฐ์ฐ์ด ๋จ์ํ๊ฑฐ๋ ๋ฐ์ดํฐ์ ํฌ๊ธฐ๊ฐ ์์ ๊ฒฝ์ฐ, ๋๊ธฐํ๊ฐ ํ์ํ ๊ฒฝ์ฐ, ์์๊ฐ ์ค์ํ ์ฐ์ฐ์ธ ๊ฒฝ์ฐ์๋ ์คํ๋ ค ์ฑ๋ฅ์ด ์ ํ๋ ์ ์๋ค.
๋ฐ๋ผ์ ๋ณ๋ ฌ ์คํธ๋ฆผ์ ๋ฐ์ดํฐ์ ํน์ฑ๊ณผ ์ฐ์ฐ์ ๋ณต์ก๋๋ฅผ ๊ณ ๋ คํ์ฌ ์ ์ ํ๊ฒ ์ฌ์ฉํ๋ ๊ฒ์ด ์ค์ํ๋ค.
