DevLog ๐ถ
[๋ชจ๋์๋ฐ์ธ์ก์ ] ์คํธ๋ฆผ์ ํ์ฉํด๋ณด์! - ์คํธ๋ฆผ ๋ฉ์๋ ๋ณธ๋ฌธ
[๋ชจ๋์๋ฐ์ธ์ก์ ] ์คํธ๋ฆผ์ ํ์ฉํด๋ณด์! - ์คํธ๋ฆผ ๋ฉ์๋
dolmeng2 2023. 4. 7. 16:02๐ฌ ๋ชจ๋ ์๋ฐ ์ธ ์ก์ ์ฑํฐ 5์ ์ฝ๊ณ ์ ๋ฆฌํ ๊ธ์ ๋๋ค.
โ๏ธ ํํฐ๋ง
๐ฑ ํ๋ฆฌ๋์ผ์ดํธ๋ก ํํฐ๋งํ๊ธฐ
List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
// ๋๋ค์ ์ธ์(๋ฐฑ์๋ ํฌ๋ฃจ)๋ฅผ ๋ฐ์ ํน์ ์์๋ฅผ ์ ํํ๋ค.
.filter(crew -> crew.getCourse() == Course.BACKEND)
.collect(Collectors.toUnmodifiableList());
์ง๋ ์ฑํฐ์์ ์ฌ์ฉํ๋ ์์ ์ด๋ค.
์ฌ๊ธฐ์ filter ๋ฉ์๋๋ Predicate<T>๋ฅผ ์ธ์๋ก ๋ฐ์์, ํด๋น ํ๋ฆฌ๋์ผ์ดํธ์ ์ผ์นํ๋ ๋ชจ๋ ์์๋ฅผ ํฌํจํ๋ ์คํธ๋ฆผ์ ๋ฐํํ๋ค.
๐ฑ ๊ณ ์ ์์ ํํฐ๋ง
distinct ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ค๋ณต์ ์ ๊ฑฐํ ์์๋ก ์ด๋ฃจ์ด์ง ์คํธ๋ฆผ์ ๋ฐํํ๋ค.
์ด๋, ์ค๋ณต ์ฌ๋ถ๋ hashCode์ equals๋ฅผ ํตํด ๊ฒฐ์ ๋๋ค. (์ฌ์ ์ ํ์!)
List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.distinct()
.collect(Collectors.toUnmodifiableList());
โ๏ธ ์คํธ๋ฆผ ์ฌ๋ผ์ด์ฑ
๐ฑ takeWhile
์๋ฐ9์์๋ takeWhile, dropWhile์ด๋ผ๋ ๊ฒ์ ํตํด์ ์คํธ๋ฆผ์ ์์๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ ํํ ์ ์๋๋ก ์ง์ํ๋ค.
List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
.filter(crew -> crew.getAge() <= 25)
.collect(Collectors.toUnmodifiableList());
์ฐ์ , ์์ ๋งํ filter๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด ์ ์ฒด ์คํธ๋ฆผ์ ๋ฐ๋ณตํ๋ฉด์ ๊ฐ๊ฐ์ ์์์ Predicate<T>๋ฅผ ์ ์ฉํ๋ค.
๊ทธ๋ฌ๋, ๋ง์ฝ ๋์ ๋ฆฌ์คํธ๊ฐ ์ ๋ ฌ๋์ด ์๋ค๋ฉด ์ด๋จ๊น?
predicate๋ฅผ ๋ง์กฑํ๋ ์ง์ ๊น์ง ํ์ธํ๊ณ (์ฌ๊ธฐ์๋ ๋์ด๊ฐ 25์ด ์ดํ) ๊ทธ ๋ค๋ก๋ ๋ณด์ง ์์๋ ๋๋ ๊ฒ์ด๋ค.
์ด๋ฌํ ์ฐ์ฐ์ takeWhile์ ํ์ฉํ๋ฉด ์ฝ๊ฒ ์งํํ ์ ์๋ค.
List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
.takeWhile(crew -> crew.getAge() <= 25)
.collect(Collectors.toUnmodifiableList());
takeWhile์ ์กฐ๊ฑด์ ๋ํด ์ฐธ์ด ์๋ ๊ฒฝ์ฐ ๋ฐ๋ก ๊ฑฐ๊ธฐ์ ๋ฉ์ถ๊ฒ ๋๋ค.
์ฆ, ํด๋น ๋ฆฌ์คํธ์ ๋์ด์ ๋ํด ์ ๋ ฌ๋์ด ์์ ๋ ์ฌ์ฉํด์ผ ํ๋ค๋ ์ ์ ๊ธฐ์ตํด์ผ ํ๋ค.
๐ฑ dropWhile
๋ง์ฝ, takeWhile๋ก ์ ํ๋์ง ์์ ๋ค๋ฅธ ์์๋ค์ ์ ํํ๊ธฐ ์ํด์๋ dropWhile์ ์ฌ์ฉํ๋ฉด ๋๋ค.
List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
.dropWhile(crew -> crew.getAge() <= 25)
.collect(Collectors.toUnmodifiableList());
dropWhile์ ์ฌ์ฉํ๋ฉด takeWhile๊ณผ ๋ฐ๋๋ก ์ํํ๋ค.
dropWhile์ ์ฒ์์ผ๋ก ๊ฑฐ์ง์ด ๋๋ ์ง์ ๊น์ง ๋ฐ๊ฒฌ๋ ์์๋ค์ ๋ฒ๋ฆฌ๊ณ , ๋จ์ ์์๋ค์ ๋ฐํํ๋ค.
โ๏ธ ์คํธ๋ฆผ ์ถ์
๐ฑ limit
์ฃผ์ด์ง ๊ฐ ์ดํ์ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง๋ ์๋ก์ด ์คํธ๋ฆผ์ ๋ฐํํ๋ limit(n) ๋ฉ์๋๋ ์กด์ฌํ๋ค.
์ด๋, ์คํธ๋ฆผ์ด ์ ๋ ฌ๋์ด ์์ ๋ ์ต๋ ์์ n๊ฐ๋ฅผ ๋ฐํํ๋ค.
List<Crew> crews = new ArrayList<>();
List<String> backendCrews = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.limit(10)
.collect(Collectors.toUnmodifiableList());
๋ฌผ๋ก , ์ ๋ ฌ๋์ง ์์ ์ํ์์๋ limit์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์ด๋ฌ๋ฉด ๊ฒฐ๊ณผ ์ญ์ ์ ๋ ฌ๋์ง ์์ ์ํ๋ก ๋ฐํํ๋ค.
โ๏ธ ์์ ๊ฑด๋๋ฐ๊ธฐ
๐ฑ skip
์ฒ์ ์์ n๊ฐ๋ฅผ ์ ์ธํ ์คํธ๋ฆผ์ ๋ฐํํ๋ skip(n) ๋ฉ์๋๊ฐ ์กด์ฌํ๋ค.
๋ง์ฝ, n๊ฐ ์ดํ์ ์์๊ฐ ์กด์ฌํ๋ค๋ฉด ๋น ์คํธ๋ฆผ์ด ๋ฐํ๋๊ธฐ ๋๋ฌธ์ ์คํธ๋ฆผ์ ์ฃผ์ด์ง ๊ฐ๋ณด๋ค ๋ ํฐ ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค.
List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.skip(3)
.collect(Collectors.toUnmodifiableList());
โ๏ธ ๋งคํ
๐ฑ map
์คํธ๋ฆผ์์๋ map() ๋ฉ์๋๋ฅผ ํตํด ํจ์๋ฅผ ์ธ์๋ก ๋ฐ์ ์ ์๋ค.
๋ฐ์ ํจ์๋ ๊ฐ ์์์ ์ ์ฉ๋๋ฉฐ, ์ ์ฉ๋ ๊ฒฐ๊ณผ๊ฐ ์๋ก์ด ์์๋ก ๋งคํ๋๋ค.
List<Crew> crews = new ArrayList<>();
List<String> backendCrews = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.map(Crew::getName)
.collect(Collectors.toUnmodifiableList())
์ ์ฝ๋์์ map์ ํตํด Crew ํ์ ์ String ํํ๋ก ๋ฐํํ๋๋ก ๋ง๋ค์๋ค.
๐ฑ flatMap
๋งคํ ๊ธฐ๋ฅ ์ค์์, flatMap()์ด๋ผ๋ ํน๋ณํ ๊ธฐ๋ฅ์ด ์กด์ฌํ๋ค.
์ด๋ ์คํธ๋ฆผ ๊ฐ๊ฐ์ ์์๋ฅผ ๋ค๋ฅธ ์คํธ๋ฆผ์ผ๋ก ๋ง๋ ๋ค์์, ๋ชจ๋ ์คํธ๋ฆผ์ ํ๋์ ์คํธ๋ฆผ์ผ๋ก ์ฐ๊ฒฐํ๋ค.
String[] words = {"Hello", "Wootechco"};
List<String> splitedWords =
// ๋ฐฐ์ด์ ๊ฐ ์์์ ๋ํด์ stream ์ํ
Arrays.stream(words)
// ๊ฐ ์์๋ฅผ ๋ถํดํ์ฌ ๋งคํ (H,e,l,l,o / W,o,o,t,e,c,h,c,o)
.map(word -> word.split(""))
// ์์ฑ๋ ์คํธ๋ฆผ์ ํ๋์ ์คํธ๋ฆผ์ผ๋ก ํ๋ฉดํ ์งํ, (word -> Arrays.stream(word))์ ๋์ผํ ์๋ฏธ์ด๋ค.
.flatMap(Arrays::stream)
// ๋ฆฌ์คํธ๋ก ๋ชจ์ผ๊ธฐ
.collect(Collectors.toList());
// [H, e, l, l, o, W, o, o, t, e, c, h, c, o]
System.out.println(splitedWords);
Intellij์์ ์คํธ๋ฆผ์ ๊ฐ ๋ฐํ๊ฐ์ ๋ํด ์ถ๋ก ํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์.
word.split(””) ํ์ ๋ “Hello”, “wootechco”๋ผ๋ ๊ฐ ๋ฌธ์์ด์ ๋ํ์ฌ split์ ์งํํ ๊ฒฐ๊ณผ๋ก String[]์ ๊ฐ์ง ์คํธ๋ฆผ์ ๋ฐํํ๊ฒ ๋๊ณ , ํด๋น ์คํธ๋ฆผ์ ๋ํด flatMap์ ์ฌ์ฉํ์ฌ Stream<String>์ ๋ฐํํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. (String[] -> String์ผ๋ก ํ๋ฉดํ)
โ๏ธ ๊ฒ์๊ณผ ๋งค์นญ
ํน์ ์์ฑ์ด ๋ฐ์ดํฐ ์งํฉ์ ์กด์ฌํ๋์ง์ ๋ํด Stream API๋ฅผ ํ์ฉํ๋ฉด ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
๐ฑ anyMatch
boolean isBackendCrew = crews.stream()
.anyMatch(crew -> crew.getCourse() == Course.BACKEND);
์ ์ด๋ ํ๋์ ์์์ ์ผ์นํ๋์ง ํ์ธํ๋ค.
๐ฑ allMatch
boolean isBackendCrew = crews.stream()
.allMatch(crew -> crew.getCourse() == Course.BACKEND);
๋ชจ๋ ์์์ ์ผ์นํ๋์ง ํ์ธํ๋ค.
๐ฑ noneMatch
boolean isBackendCrew = crews.stream()
.noneMatch(crew -> crew.getCourse() == Course.BACKEND);
๋ชจ๋ ์์์ ์ผ์นํ์ง ์๋์ง ํ์ธํ๋ค.
์ 3๊ฐ์ง ์ฐ์ฐ์ ๋ชจ๋ ‘์ผํธ์ํท ๊ธฐ๋ฒ’์ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ, ์ด๋ ์ ์ฒด ์คํธ๋ฆผ์ ์ฒ๋ฆฌํ์ง ์๋๋ผ๋ ๊ฒฐ๊ณผ๋ฅผ ๋์ค์ ๋ฐํํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
์๋ฅผ ๋ค์ด, ์๋ฐ์์ if (a && b)๋ผ๋ ์กฐ๊ฑด๋ฌธ์ด ์์ ๋, a๊ฐ ๊ฑฐ์ง์ด๋ฉด ๊ทธ ๋ค์ ์กฐ๊ฑด์์ ๋ณด์ง ์๊ณ ๋ฐ๋ก ์ ์ฒด์ ๋ํด ๊ฑฐ์ง์ด๋ผ๊ณ ํ๋จํ๊ฒ ๋๋ค. ์ด๋ฐ ์ํฉ์ ์ผํธ์ํท์ด๋ผ๊ณ ํ๋ฉฐ, ๋ชจ๋ ์คํธ๋ฆผ์ ์์์ ๋ํด์ ํ๋จํ์ง ์๊ณ , ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ์์ผ๋ฉด ๋ฐ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๊ฒ์ ์๋ฏธํ๋ค. (limit ์ญ์ ์ผํธ์ํท ์ฐ์ฐ์ด๋ค)
๐ฑ findAny
Optional<Crew> backendCrew = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.findAny()
์คํธ๋ฆผ์์ ์์์ ์์๋ฅผ ๋ฐํํ๋ค.
filter + findAny๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด, ํด๋น ์กฐ๊ฑด๊ณผ ์ผ์นํ๋ ์์์ ์์๋ฅผ ๋ฆฌํดํ๋ค.
์ด๋, ์์๊ฐ ์กด์ฌํ์ง ์์ ์ ์๊ธฐ ๋๋ฌธ์ Optional<T>์ ์ฌ์ฉํ ์ ์๋ค.
Optional์ ํ์ฉํ๋ฉด ๊ฐ์ด ์กด์ฌํ๊ฑฐ๋, ํน์ ์กด์ฌํ์ง ์์์ ๋ช ์์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค.
์ฌ๊ธฐ์ ์ฒด์ด๋์ ํตํด ifPresent๋ isPresent๋ฅผ ํตํด ๊ฐ์ด ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ์ ๋ํด ์ฒ๋ฆฌํ ์ ์๋ค. (์ถํ ์ฑํฐ๋ฅผ ์งํํ๋ฉฐ ๋์จ๋ค!)
boolean isPresent = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.findAny()
.isPresent();
// ์กด์ฌํ๋ค๋ฉด ๋ณ๋์ ๋ก์ง ์คํ
crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.findAny()
.ifPresent(crew -> System.out.println(crew.getName()));
// ๊ฐ์ด ์กด์ฌํ๋ฉด ๊ฐ์ ธ์ค๊ณ , ์๋๋ผ๋ฉด NoSuchElementException
Crew backendCrew = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.findAny()
.get();
// ๊ฐ์ด ์์ผ๋ฉด ๋ฐํํ๊ณ , ์๋๋ผ๋ฉด ์ค์ ํ ๊ธฐ๋ณธ๊ฐ ๋ฐํ
Crew defaultCrew = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.findAny()
.orElse(new Crew());
๋์ถฉ ์์ ์์์ฒ๋ผ ๊ฐ์ด ์กด์ฌํ ๋, ํน์ ์กด์ฌํ์ง ์์ ๋ ๋ณ๋์ ์กฐ๊ฑด์ ์ฃผ์ด์ ์กฐ๊ธ ๋ ์ฉ์ดํ๊ฒ ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ค๋ค.
๐ฑ findFirst
๋ง์ฝ, ์คํธ๋ฆผ ๋ด๋ถ์ ์์ดํ ์์๊ฐ ์ ํด์ ธ ์๋ค๋ฉด ์ด๋จ๊น? findFirst()๋ฅผ ํ์ฉํ๋ฉด ์ผ์นํ๋ ์์ ์ค ๊ฐ์ฅ ์ฒซ ๋ฒ์งธ ๊ฐ์ ๋ฐํํ๋ค.
Optional<Crew> backendCrew = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.findFirst();
๊ทธ๋ ๋ค๋ฉด, ์ด๋ฌํ ์๋ฌธ์ด ๋ค ๊ฒ์ด๋ค. findFirst์ findAny ๋ชจ๋ ๋์ผํ Optional<Crew>๋ฅผ ๋ฐํํ๋๋ฐ, ๋ฌด์จ ์ฐจ์ด๊ฐ ์์๊น?
๋ฐ๋ก, ๋ณ๋ ฌ์ฑ์์ ์ฐจ์ด๊ฐ ์๋ค.
๋ณ๋ ฌ ์คํ์์๋ ์ฒซ ๋ฒ์งธ ์์๊ฐ ์ด๋ค ๊ฒ์ธ์ง ์๊ธฐ๊ฐ ์ด๋ ต๊ธฐ ๋๋ฌธ์, ์์์ ๋ฐํ ์์๊ฐ ์๊ด์๋ค๋ฉด ๋ณ๋ ฌ ์คํธ๋ฆผ์์๋ (parallelStream) findAny()๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
// Stream.java
@Override
public final Optional<P_OUT> findFirst() {
return evaluate(FindOps.makeRef(true));
}
@Override
public final Optional<P_OUT> findAny() {
return evaluate(FindOps.makeRef(false));
}
์ค์ ๋ก ๋์ ๋ด๋ถ ๊ตฌํ์ ๋ณด๋ฉด, ์ธ์๋ก mustFindFirst๋ผ๋ boolean๊ฐ์ ๋ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ๋ด๋ถ์ ์ผ๋ก ์ด๋ ํ ๋ก์ง์ ํตํด์ ์ฒซ ๋ฒ์งธ ๊ฐ์ ๋ฐํํ๊ฒ ๋๋ค.
Params: mustFindFirst – whether the TerminalOp must produce the first element in the encounter order
์ฃผ์์ ์ ํ ์ค๋ช ์ ๋ณด๋ฉด, ์ฃผ์ด์ง ์์์์ ๊ฐ์ฅ ์ฒ์์ผ๋ก ๋ฐ๊ฒฌ๋ ๊ฐ์ ๋ฆฌํดํ๊ฑฐ๋, ์๋๋ฉด ๊ทธ๋ฅ ์๋ฌด ์์๋ ๋ฆฌํดํจ์ ์ ์ ์๋ค.
โ๏ธ ๋ฆฌ๋์ฑ
์คํธ๋ฆผ ์์๋ฅผ ์กฐํฉํ์ฌ ๋ ๋ณต์กํ ์ง์๋ฅผ ํํํ๊ธฐ ์ํด์ ๋ฆฌ๋์ฑ ์ฐ์ฐ์ ์ฌ์ฉํ ์ ์๋ค.
์ด๋ ๋ชจ๋ ์คํธ๋ฆผ ์์๋ฅผ ์ฒ๋ฆฌํ์ฌ ํ๋์ ๊ฐ์ผ๋ก ๋์ถํ๋ ๊ฒ์ ์๋ฏธํ๋ฉฐ, ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์์๋ ‘ํด๋’๋ผ๊ณ ๋ ๋งํ๋ค.
๋ฆฌ์คํธ์ ์กด์ฌํ๋ ๋ชจ๋ ์ซ์๋ฅผ ๋ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
// before
int sum = 0;
for(int number : numbers) {
sum += number;
}
// after
sum = numbers.stream().reduce(0, (a, b) -> a + b);
// ๋ฉ์๋ ์ฐธ์กฐ
sum = numbers.stream().reduce(0, Integer::sum);
for-each ๋์ ์ reduce๋ฅผ ์ฌ์ฉํ๋ฉด, ์ฝ๋๊ฐ ํจ์ฌ ๊ฐ๊ฒฐํด์ง๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๐ฑ reduce
T reduce(T identity, BinaryOperator<T> accumulator);
Stream ์ธํฐํ์ด์ค์ ์ ์๋ reduce๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑ๋๋ค.
์ฒซ ๋ฒ์งธ ์ธ์๋ก๋ ์ด๊น๊ฐ์, ๋ ๋ฒ์งธ ์ธ์๋ก๋ ๋ ๊ฐ์ ์์๋ฅผ ์กฐํฉํ์ฌ ์๋ก์ด ๊ฐ์ ๋ง๋ค๋๋ก BinaryOperator<T>๋ฅผ ์ฌ์ฉํ๋ค. (์ด๋, ๋ ์์๋ ๋ชจ๋ ๊ฐ์ ํ์ ์ด์ด์ผ ํ๋ค)
sum = numbers.stream().reduce(0, (a, b) -> a + b);
์ฐ๋ฆฌ์ ์ฝ๋์์๋ ์ด๊ธฐ๊ฐ์ผ๋ก 0์, ๊ทธ๋ฆฌ๊ณ ์๋ก์ด ๊ฐ์ ๋ง๋ค๊ธฐ ์ํ ์คํผ๋ ์ดํฐ๋ (a, b) → a + b๋ฅผ ์ฌ์ฉํ์๋ค.
๋ง์ ์ด ์๋ ๊ณฑ์ , ๋บ์ ๋ฑ์ผ๋ก๋ ์์ ๋กญ๊ฒ ์์ฉ์ด ๊ฐ๋ฅํ๋ค.
reduce๋ ์ด๋ค ๋ฐฉ์์ผ๋ก ์คํธ๋ฆผ์ ๋ชจ๋ ์ซ์๋ฅผ ๋ํ๊ฒ ๋ ๊น?
์ด๊ธฐ๊ฐ์ผ๋ก๋ถํฐ, ์คํธ๋ฆผ์ ๊ฐ ์์๋ฅผ ํ๋์ฉ ์๋นํ๋ฉฐ ๊ทธ ๊ฒฐ๊ณผ๊ฐ์ ๊ทธ ๋ค์ ์ฐ์ฐ์ ์ํ ๋์ ๊ฐ์ผ๋ก ์ฌ์ฉํ๋ค.
๊ทธ๋ฆฌ๊ณ , ํด๋น ๋์ ๊ฐ๊ณผ ๊ทธ ๋ค์ ์์๋ฅผ ํจ๊ป ๋๋ค์์ ์กฐํฉํ์ฌ ๊ณ์ ๊ณ์ฐํด๋๊ฐ๋ ํ์์ด๋ค.
์ฐธ๊ณ ๋ก, ์ด๊ธฐ๊ฐ์ ์ ์ธํ์ง ์์๋ ๋๋ค. ๋จ, ์ด ๊ฒฝ์ฐ๋ Optional ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
Optional<T> reduce(BinaryOperator<T> accumulator);
์คํธ๋ฆผ์ ์๋ฌด ์์๋ ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ, ์ด๊ธฐ๊ฐ๋ ์๋ค๋ฉด ํฉ๊ณ๋ฅผ ๋ฐํํ ์ ์๊ธฐ ๋๋ฌธ์ Optional์ ํตํด null์ ๋ํ ์กด์ฌ ์ฌ๋ถ๋ฅผ ๋ํ๋ธ๋ค.
๋ํ, reduce๋ฅผ ์ด์ฉํ๋ฉด ์ต๋๊ฐ๊ณผ ์ต์๊ฐ๋ ์ฝ๊ฒ ๊ตฌํ ์ ์๋ค.
int max = numbers.stream().reduce(0, Integer::max);
int min = numbers.stream().reduce(0, Integer::min);
Optional<Integer> max = numbers.stream().reduce(Integer::max);
Optional<Integer> min = numbers.stream().reduce(Integer::min);
๐ฑ reduce์ ์ฅ์
์ด๋ฌํ reduce๋ฅผ ์ฌ์ฉํ๋ฉด ๋ญ๊ฐ ์ข์๊น?
์ฐ์ , ๋ด๋ถ ๋ฐ๋ณต์ผ๋ก ์ธํด ๋ฐ๋ณตํ๋ ๋ก์ง์ด ์ถ์ํ๋๋ฉด์, reduce ์์ฒด๋ฅผ ๋ด๋ถ์ ์ผ๋ก ๋ณ๋ ฌ๋ก ์คํํ ์ ์๊ฒ ๋๋ค๋ ๊ฒ์ด๋ค.
์์์๋ถํฐ ๊ณ์ ๋ณด์๋ ํฉ์ ๊ตฌํ๋ reduce๋ฅผ ์๊ฐํด๋ณด์.
์ฐ๋ฆฌ๊ฐ ์ธ๋ถ ๋ฐ๋ณต์ ํตํด ์์์ ํฉ์ ๊ตฌํ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ ๊ฒ์ด๋ค.
List<Integer> numbers = List.of(1, 2, 3, 4);
int sum = 0;
for (Integer number: numbers) {
sum += number;
}
์ฌ๊ธฐ์ ๊ณต์ ๋ณ์์ธ sum์ด ์๊ธฐ ๋๋ฌธ์, ์ฐ๋ฆฌ๊ฐ ์ ๋ก์ง์ ๋ณ๋ ฌ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ถ๋ค๋ฉด ๊ณต์ ๋ณ์์ ๋ํ ๋๊ธฐํ ์ฒ๋ฆฌ๋ฅผ ํด์ค์ผ ํ๋ค.
ํ์ง๋ง, stream์ ์ฌ์ฉํ๊ฒ ๋๋ค๋ฉด ๋จ์ํ parallelStream()์ ์ฌ์ฉํ๋ ๊ฒ๋ง์ผ๋ก๋ ๋ณ๋ ฌ์ฑ์ ์ฝ๊ฒ ์ป์ ์ ์๊ฒ ๋๋ค.
๋ํ, reduce๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ค๋ฉด ๊ณต์ ๋ณ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ์์ ๊ฐ์ด ํฉ์ ๊ตฌํ๋๋ผ๋ ๋ณ๋ ฌ๋ก ์คํ์ด ๊ฐ๋ฅํ๊ฒ ๋๋ค.
(์ด์ ๋ํด์๋ ๊ฐ๋ณ๋ก ํฌ์คํ ์ ์ฌ๋ ค๋ณด๋๋ก ํ๊ฒ ๋ค :D)
๐ฑ ์คํธ๋ฆผ์ ์ํ
map, filter ๋ฑ์ ์ ๋ ฅ ์คํธ๋ฆผ์์ ์์๋ฅผ ๋ฐ์ ์ถ๋ ฅ ์คํธ๋ฆผ์ผ๋ก ๋ณด๋ด๊ธฐ ๋๋ฌธ์, ๋ด๋ถ์ ์ผ๋ก ์ํ๋ฅผ ๊ฐ์ง๊ณ ์์ง ์๋ค.
์์๊ฐ ์ฒ๋ฆฌ๋ ๋๋ง๋ค ๋ฐ๋ก๋ฐ๋ก ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ๊ธฐ ๋๋ฌธ์, ์ถ๋ ฅ ์คํธ๋ฆผ์ ์์ ๊ฐ์์ ์ ๋ ฅ ์คํธ๋ฆผ์ ์์ ๊ฐ์๊ฐ ๋์ผํ๊ฒ ์ ์ง๋๋ค.
ํ์ง๋ง, sort, distinct ๋ฑ์ ์์๋ฅผ ์ ๋ ฌํ๊ฑฐ๋ ์ค๋ณต์ ์ ๊ฑฐํ๊ธฐ ์ํด์ ๊ณผ๊ฑฐ์ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค.
์ด๋ฐ ๊ฒฝ์ฐ ‘์ํ๊ฐ ์๋ ์คํธ๋ฆผ’์ด๋ผ๊ณ ํ๋ฉฐ, ์ํ๊ฐ ์๋ ์คํธ๋ฆผ ๋ชจ๋ ์์๊ฐ ๋ฒํผ์ ์ฌ์ ์ ์ถ๊ฐ๊ฐ ๋์ด ์์ด์ผ ํ๊ธฐ ๋๋ฌธ์, ์คํธ๋ฆผ ๋ฐ์ดํฐ์ ํฌ๊ธฐ๊ฐ ํฌ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
๋ํ, reduce, sum ๊ฐ์ ์ฐ์ฐ์ ๋ด๋ถ์ ์ผ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ด๋ถ ์ํ์ ํฌ๊ธฐ๋ ํ์ ๋์ด (bound) ์์ด์ผ ํ๋ค.
์ฐ๋ฆฌ๋ ์ ์ฝ๋์์ Integer::max ๋ฑ์ ํ์ฉํด์ ๋ด๋ถ์ ๊ฐ์ด ์ต๋ Integer ๋ฒ์ ๋ด์ ๊ฐ์์ ํ์ ํ ์ ์ด๋ค.
๊ทธ๋์ ์ํ๊ฐ ์๋ ์คํธ๋ฆผ์ ์ํ๊ฐ ์๋ ์คํธ๋ฆผ๋ณด๋ค ์ฒ๋ฆฌ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์์ผ๋ฉฐ, ๋ด๋ถ ํฌ๊ธฐ ์ ์ฝ์ด ํ์ํ๋ค.
์ด๋ฌํ ์ ์ ์ ๊ณ ๋ คํ์ฌ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค.
โ๏ธ ์ซ์ํ ์คํธ๋ฆผ
int ages = crews.stream()
.map(Crew::getAge)
.reduce(0, Integer::sum);
์ ์ฝ๋์์๋ Integer::sum์ด๋ผ๋ ๋ฉ์๋ ์ฐธ์กฐ ์ฝ๋๋ก ์ธํด์ int → Integer๋ก์ ๋ฐ์ฑ ๋น์ฉ์ด ์์๋๋ค.
๐ก ๋ฐ์ฑ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์ด๊ธฐํํ๋ ๊ณผ์ ์ด ํ์ํ๊ธฐ ๋๋ฌธ์, ์ด ๊ณผ์ ์์ ๋ฉ๋ชจ๋ฆฌ ํ ๋น, ์์ฑ์ ํธ์ถ ๋ฑ์ ๋น์ฉ์ด ๋ฐ์ํ๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด, ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์ ํ์ฉํด๋ณด์.
๊ธฐ๋ณธ ์คํธ๋ฆผ์์ ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์ผ๋ก ๋ณํํ๊ธฐ ์ํด์๋ ๊ฐ ํ์ ์ ๋ง์ถฐ mapToInt, mapToDouble, mapToLong 3๊ฐ์ง ๋ฉ์๋๋ฅผ ํ์ฉํ ์ ์๋ค.
int ages = crews.stream()
.mapToInt(Crew::getAge)
.sum();
์ ์์ ์์๋ mapToInt๋ฅผ ํ์ฉํด์ IntStream์ ๋ฐํํ์ฌ (๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ) ํด๋น ์คํธ๋ฆผ์์ ์ ๊ณตํ๋ sum ๋ฉ์๋๋ฅผ ํ์ฉํ์ฌ ํฉ์ ๊ณ์ฐํ์๋ค. ์ด๋, ๋ง์ฝ IntStream์ด ๋น์ด์์ผ๋ฉด sum์ ๊ธฐ๋ณธ๊ฐ์ธ 0์ ๋ฐํํ๊ฒ ๋๋ฉฐ, sum ์ธ์๋ ๋ค์ํ ์ ํธ๋ฆฌํฐ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์์ ๊ธฐ๋ณธ ์คํธ๋ฆผ์ผ๋ก ๋ณต์ํ๊ธฐ ์ํด์๋, boxed ๋ฉ์๋๋ฅผ ํ์ฉํ๋ฉด ๋๋ค.
Stream<Integer> stream = crews.stream()
.mapToInt(Crew::getAge)
.boxed();
๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์์ max๋ฅผ ์ฌ์ฉํ ๋, ๊ฐ์ด ์๋ ๊ฒฝ์ฐ์ ์ต๋๊ฐ์ด 0์ธ ๊ฒฝ์ฐ ๋ชจ๋ 0์ ๋์ถํ๋ค.
์ด๋, ๋์ ์ด๋ป๊ฒ ๊ตฌ๋ณํ ์ ์์๊น? ์ด๋๋ Optional์ ํ์ฉํ์.
OptionalInt ages = crews.stream()
.mapToInt(Crew::getAge)
.max();
// ์ต๋๊ฐ์ด ์๋ ์ํฉ์ ๋ํด ๊ธฐ๋ณธ๊ฐ ์ ์
int ages = crews.stream()
.mapToInt(Crew::getAge)
.max()
.orElse(10);
์ด๋ ๊ฒ ํ๋ฉด IntStream์ ์ต๋๊ฐ์ ํ์คํ๊ฒ ์ฒดํฌํ ์ ์์ผ๋ฉฐ, ์ต๋๊ฐ์ด ์๋ ์ํฉ์ ๋ํด์๋ ๋ช ์์ ์ผ๋ก ์ ์๊ฐ ๊ฐ๋ฅํ๋ค.
ํน์ ๋ฒ์์ ์ซ์๋ฅผ ์ฌ์ฉํด์ผ ํ ๋๋, range๋ฅผ ํ์ฉํด๋ณด์.
// ์์-์ข
๋ฃ๊ฐ์ด ๊ฒฐ๊ณผ์ ํฌํจํ์ง ์์
IntStream range = IntStream.range(1, 100);
// ์์-์ข
๋ฃ๊ฐ์ด ๊ฒฐ๊ณผ์ ํฌํจ๋จ
IntStream range = IntStream.rangeClosed(1, 100);
โ๏ธ ์คํธ๋ฆผ ์์ฑํ๊ธฐ
๐ฑ ๊ฐ → ์คํธ๋ฆผ : of
Stream<String> stream = Stream.of("Modern ", "Java ", "In ", "Action");
stream.map(String::toUpperCase)
.forEach(System.out::println); // MODERN ,JAVA ,IN ,ACTION
// ์คํธ๋ฆผ ๋น์ฐ๊ธฐ
stream.empty();
๊ฐ๊ฐ์ ๊ฐ๋ค์ ๋ํด์ ์คํธ๋ฆผ์ผ๋ก ์์ฑํ ์ ์๋ค.
๐ฑ null์ด ๋ ์ ์๋ ๊ฐ์ฒด๋ฅผ ์คํธ๋ฆผ์ผ๋ก ์์ฑ : ofNullable
// Before
String homeValue = System.getProperty("home");
Stream<String> homeValueStream
= homeValue == null ? Stream.empty() : Stream.of(value);
// After
// ofNullable์ ์ฌ์ฉํ๋ฉด ์์ฑํ ์ ์๋ค.
Stream<String> homeValueStream
= Stream.ofNullable(System.getProperty("home"));
๊ธฐ์กด ์ฝ๋์์๋ ์ง์ null์ ์ฒดํฌํด์ค ๋ค์์ null์ด๋ฉด ๋น ์คํธ๋ฆผ, ์๋๋ฉด ๊ฐ์ ๋ฃ์ ์คํธ๋ฆผ์ ํด์คฌ์ด์ผ ํ๋ค.
ofNullable์ ํ์ฉํ๋ฉด ์์์ ๊ธฐ์กด ์ฝ๋์ ๋ํ ๋ถ๋ถ์ ๋ด๋ถ์ ์ผ๋ก ์ฒ๋ฆฌํด์ค๋ค.
public static<T> Stream<T> ofNullable(T t) {
return t == null ? Stream.empty()
: StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
์ด๋ฐ ์์ผ๋ก!
๐ฑ ๋ฐฐ์ด → ์คํธ๋ฆผ : Arrays.stream
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
์ด๋, int[] ๊ฐ์ primitive type์ ๋ํด์๋ ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์ ๋ฐํํ๋ค.
๐ฑ ํ์ผ → ์คํธ๋ฆผ (nio API)
long uniqueWords = 0;
// try-with-resources
try(Stream<String> lines =
Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
// split์ ํธ์ถํ์ฌ ๊ฐ ๋จ์ด ๋ถ๋ฆฌ
// flatMap์ ํตํด์ ํ๋๋ก ํ๋ฉดํ
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
} catch(IOException e){
}
๐ฑ ํจ์ → ๋ฌดํ ์คํธ๋ฆผ : iterate
// UnaryOperator<T>
Stream.iterate(0, n -> n + 2)
// ๋ฌดํ์ผ๋ก ๊ฐ์ ์์ฑํ ์ ์๊ธฐ ๋๋ฌธ์ (์ธ๋ฐ์ด๋ ์คํธ๋ฆผ) ๊ฐ์ ์ ํ
.limit(10)
.forEach(System.out::println); // 0, 2, 4...
๋ฌดํ ์คํธ๋ฆผ์ ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋์ง ์์ ์คํธ๋ฆผ์ ์๋ฏธํ๋ค. ๋ณดํต limit์ผ๋ก ์คํธ๋ฆผ์ ์ต๋ ์ฌ์ด์ฆ๋ฅผ ์ ํํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ค.
๋ง์ฝ ์ ํํ์ง ์๊ฒ ๋๋ค๋ฉด ์ค๊ฐ ์ฐ์ฐ์ ์คํธ๋ฆผ์ ์์๋ฅผ ์๋นํ์ง ์๊ธฐ ๋๋ฌธ์, ์๋ฌด ๊ฒฐ๊ณผ๋ ๊ณ์ฐ๋์ง ์๋๋ค.
๋ํ, ๋ฌดํ์ด๊ธฐ ๋๋ฌธ์ ์ ๋ ฌํ๊ฑฐ๋ reduce ์ฐ์ฐ์ ์ํํ ์ ์๋ค.
public static<T> Stream<T> iterate(T seed,
Predicate<? super T> hasNext,
UnaryOperator<T> next) {
...
}
iterate ๋ฉ์๋์ ๋ด๋ถ์ ์ผ๋ก Predicate๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์, ํน์ ์กฐ๊ฑด์ ๋ง๋ ์ซ์๋ฅผ ์์ฑํ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค.
// 100 ์ด๋ด์ ๊ฐ์ 0๋ถํฐ ์์ํด์ 4์ฉ ์ฆ๊ฐํ์ฌ ์ถ๋ ฅ
IntStream.iterate(0, n -> n < 100, n -> n + 4)
.forEach(System.out::println);
// ํน์, takeWhile์ ์ฌ์ฉํ ์ ์๋ค.
// filter๋ ์ธ์ ์ค๋จํด์ผ ํ ์ง ์ ์ ์๊ธฐ ๋๋ฌธ์ filter๋ก๋ ๋์ฒด๋์ง ์๋๋ค.
IntStream.iterate(0, n -> n + 4)
.takeWhile(n -> n < 100)
.forEach(System.out::println);
์์ ์์ฑํ ๊ฒ์ฒ๋ผ, ํน์ ์กฐ๊ฑด์ ์ค ๋ filter๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ผํธ ์ํท ๊ธฐ๋ฒ์ ์ฌ์ฉํ๋ takeWhile ๊ธฐ๋ฅ์ ํตํด์ ์ข ๋จ ์ง์ ์ด ํ์คํ๋๋ก ์กฐ๊ฑด์ ์ฃผ์ด์ผ ํ๋ค.
๐ฑ ํจ์ → ๋ฌดํ ์คํธ๋ฆผ : generate
generate๋ iterate์ ๋ค๋ฅด๊ฒ, ์์ฐ๋ ๊ฐ์ ๋ํ ์ฐ์์ฑ์ ๋ณด์ฅํ์ง ์๋๋ค.
// 0~1 ์ฌ์ด์ ์์์ ์ซ์ 5๊ฐ ์์ฑ
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
generate ๋ด๋ถ์์๋ Supplier๋ฅผ ๋ฐ๋๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก, ๋ฐ์ฑ ์ฐ์ฐ์ ํผํ๊ธฐ ์ํด์๋ ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์ ์ฌ์ฉํ๋ฉด ๋๋ค.
// 0~1 ์ฌ์ด์ ์์์ ์ซ์ 5๊ฐ ์์ฑ
DoubleStream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
์์ ๋งํ๋ ๊ฒ์ฒ๋ผ generate๋ ์ธ์๋ก supplier๋ฅผ ๋ฐ๋๋ค.
supplier๋ฅผ ํตํด์ ์ด๋ ํ ์กฐ์์ ํตํด ์คํธ๋ฆผ์ ์กด์ฌํ๋ ๋ค์ ๊ฐ์ ์์ฑํ ๋ ์ํ๋ฅผ ๋ณ๊ฒฝํ ์๋ ์๊ฒ ๋๋ค.
IntSupplier fib = new IntSupplier() {
private int previous = 0;
private int current = 1;
public int getAsInt() {
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
IntStream.generate(fib).limit(10).forEach(System.out::println);
๊ต์ฌ์์ ์ ์๋ ํผ๋ณด๋์น ์์ด์ ์ํ Supplier๋ค.
Supplier ๋ด๋ถ์์ 'previous', 'current' ๊ฐ์ ๋ณ์๋ฅผ ์ ์ธํ ๋ค์์, ๋ค์ ์์๋ค์ ๊ณ์ฐํ ๋ ํด๋น ๋ณ์์ ๊ฐ์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด๋ ๊ฒ ๋๋ฉด, ๊ฐ์ฒด๊ฐ ์์ฑ๋์์ ๋ ๊ธฐ์กด์ ์์๊ฐ ์ํฅ์ ์ฃผ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ณ ์ํ ๊ฐ์ฒด๊ฐ ๋๋ค.
๋ง์ฝ, ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ๊ฒ ๋๋ค๋ฉด previous์ current์ ๊ฐ์ด ์์ฐจ์ ์ผ๋ก ์ฆ๊ฐํ๋ ํํ๊ฐ ์๋๊ฒ ๋ ๊ฒ์ด๋ค.
๊ทธ๋์ ๋ณ๋ ฌ ์ํ์์๋ ์์ ์ฝ๋์ฒ๋ผ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์๊ฒ ๋ง๋๋ ๊ฒ์ด ์๋๋ผ, ์ด๋ ํ ์ํ์๋ ์์กดํ์ง ์๋๋ก ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค.
๋ฐ๋ฉด์ iterate์ ๊ฒฝ์ฐ ์ํ์ ๋ํ ์กฐ์์ ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ๋น๊ต์ ๋ณ๋ ฌ ์คํ์ ์์ ํ๋ค๊ณ ๋ณผ ์ ์๋ค. (๋ถ๋ณ์ฑ ๋ณด์ฅ)
์ด๋ฒ ์ฑํฐ์ ๋ด์ฉ์ ๊ฐ๋ตํ๊ฒ ์ ๋ฆฌํด๋ณด์.
- filter, distinct, takeWhile, dropWhile, skip, limit์ผ๋ก ์คํธ๋ฆผ์ ํํฐ๋งํ๊ฑฐ๋ ์๋ฅผ ์ ์๋ค.
- map, flatMap์ผ๋ก ์คํธ๋ฆผ์ ์์๋ฅผ ์ถ์ถํ๊ฑฐ๋ ๋ณํํ ์ ์๋ค.
- findFirst, findAny๋ก ์คํธ๋ฆผ์ ์์๋ฅผ ๊ฒ์ํ ์ ์๋ค.
- allMatch, noneMatch, anyMatch๋ก ํ๋ฆฌ๋์ผ์ดํธ์ ์ผ์นํ๋ ์์๋ฅผ ๊ฒ์ํ ์ ์๋ค.
- reduce๋ฅผ ํตํด ๋ชจ๋ ์์๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ์กฐํฉํ์ฌ ๊ฐ์ ๋์ถํ ์ ์๋ค.
- iterate, generate๋ก ๋ฌดํ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ค.
- ๋ฐ์ฑ ๋น์ฉ์ ์ค์ด๊ธฐ ์ํ ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ (IntStream, DoubleStream, LongStream)์ ํ์ฉํ ์ ์๋ค.
[์ํ ์๋ ์ฐ์ฐ] filter, map
[์ํ ์๋ ์ฐ์ฐ] reduced, sorted, distinct