์คํธ๋ฆผ์ ๊ทธ์ ๋ ํ๋์ API๊ฐ ์๋, ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๊ธฐ์ดํ ํจ๋ฌ๋ค์์ด๊ธฐ ๋๋ฌธ์ด๋ค. ์คํธ๋ฆผ์ด ์ ๊ณตํ๋ ํํ๋ ฅ, ์๋, ์ํฉ์ ๋ฐ๋ผ์๋ ๋ณ๋ ฌ์ฑ์ ์ป์ผ๋ ค๋ฉด API๋ ๋งํ ๊ฒ ๋ ์๊ณ ์ด ํจ๋ฌ๋ค์๊น์ง ํจ๊ป ๋ฐ์๋ค์ฌ์ผ ํ๋ค.
- ์คํธ๋ฆผ ํจ๋ฌ๋ค์์ ํต์ฌ์ ๊ณ์ฐ์ ์ผ๋ จ์ ๋ณํ(transformation)์ผ๋ก ์ฌ๊ตฌ์ฑ ํ๋ ๋ถ๋ถ์ด๋ค.
- ์ด๋ ๊ฐ ๋ณํ ๋จ๊ณ๋ ๊ฐ๋ฅํ ํ ์ด์ ๋จ๊ณ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ์ฒ ๋ฆฌ ํ๋ ์์ ํจ์์ฌ์ผ ํ๋ค.
{% hint style="info" %}
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ(functional programming)์ ์๋ฃ ์ฒ๋ฆฌ๋ฅผ ํจ์์ ๊ณ์ฐ์ผ๋ก ์ทจ๊ธํ๊ณ
์ํ์ ๊ฐ๋ณ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ฆฌํ๋ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์
{% endhint %}
๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ์์์ ์ธ ๊ณ์ฐ ์ฝ๋๊ฐ ์๋ ์ ํํ๋ ํจ์๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ด๋ค. ์ฌ์ฉํ๋ ํจ์๋ ์ํ ๊ฐ์ด๋ ๊ฐ๋ณ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ฆฌํ๋๋ก ๊ตฌํํด์ผํ๋๋ฐ ์ด๋ฌํ ํจ์๋ฅผ ์์ ํจ์๋ผ๊ณ ํ๋ค.
์์ ํจ์๋ ์ค์ง ์ ๋ ฅ๋ง์ด ๊ฒฐ๊ณผ์ ์ํฅ์ ์ฃผ๋ ํจ์ ์ฆ, ๋ค๋ฅธ ๊ฐ๋ณ ์ํ๋ฅผ ์ฐธ์กฐํ์ง ์๊ณ , ํจ์ ์ค์ค๋ก๋ ๋ค๋ฅธ ์ํ๋ฅผ ๋ณ๊ฒฝํ์ง ์๋ ํจ์๋ฅผ ์์ ํจ์๋ผ ํ๋ค.
์์ ํจ์๋ ๊ฐ๋ณ ๋ฐ ์ํ ๊ฐ์ ์ฐธ์กฐํ์ง ์๊ธฐ์ ์ ๋ ฅ ๊ฐ์ ์ํด ๊ฒฐ๊ณผ ๊ฐ์ด ์ ํด์ง๋ ํน์ฑ์ ๊ฐ๋๋ค. ๋ง์ฝ ์ํ ๊ฐ์ ์ฐธ์กฐํ๊ฒ ๋๋ค๋ฉด ์ ๋ ฅ ๊ฐ์ ์ํด ๊ฒฐ๊ณผ ๊ฐ์ด ๋ฌ๋ผ์ง๋ '๋ถ์์ฉ'์ด ๋ฐ์ํ ์ ์๋ค. ๊ฒฐ๊ตญ ์ด๋ฒ ์ฃผ์ ์ธ ๋ถ์์ฉ ์๋ ํจ์๋ ์์ ํจ์๋ฅผ ๋งํ๋ค.
์์ ํจ์๋ ๊ฐ๋ฐ์๊ฐ ์ปค์คํ ํ์ฌ ๋ง๋ค ์๋ ์์ง๋ง, ์คํธ๋ฆผ API์์ ์ ๊ณตํ๋ ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ค. ์คํธ๋ฆผ์์ ์ ๊ณตํ๋ ๊ณต์์ ์ธ ํจ์์ด๋ฏ๋ก ๋ถ์์ฉ์ด ์๊ณ , ์ฑ๋ฅ ์ต์ ํ๊ฐ ๋์ด์์ผ๋ฉฐ, 40๊ฐ์ง ์ด์์ ๋ค์ํ ํจ์๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋จผ์ ์คํธ๋ฆผ API๋ฅผ ์ฌ์ฉํ์ง๋ง, ์ ์๋ ์คํธ๋ฆผ ์ฝ๋๋ผ๊ณ ์ธ์ ํ์ง ์๋ ์ฝ๋๋ฅผ ์ดํด๋ณด์.
๋ค์์ ํ ์คํธ ํ์ผ์์ ๋จ์ด๋ณ ์๋ฅผ ์ธ์ด ๋น๋ํ๋ก ๋ง๋๋ ์ฝ๋์ด๋ค. ์คํธ๋ฆผ, ๋๋ค, ๋ฉ์๋ ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํ๊ณ , ๊ฒฐ๊ณผ๋ ์ฌ๋ฐ๋ฅด์ง๋ง ์ด๋ฅผ ์คํธ๋ฆผ ์ฝ๋๋ผ ํ์ง ์๋๋ค. ์คํธ๋ฆผ์ ์๋ชป ์ฌ์ฉํ๊ณ , ์ฌ์ฉํ์ง ์์์ ๋๋ณด๋ค ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๊ธฐ ๋๋ฌธ์ด๋ค.
์คํธ๋ฆผ ๋ด๋ถ์์ ์ธ๋ถ ์ํ(
freq)๋ฅผ ๋ณ๊ฒฝํ๋ ์์ ์ ์ํํ๊ณ ์๋ค๋ ์ ์ด๋ค. ์ด๋ ์คํธ๋ฆผ์ ์ฃผ์ ์ค๊ณ ์ฒ ํ์ธ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ด๊ธ๋๋ค. ์คํธ๋ฆผ์ ๋ฐ์ดํฐ์ ๋ณํ(Transformation)๊ณผ ํ๊ฐ(Evaluation)๋ฅผ ์ํํ๋ ๋ฐ ์ด์ ์ด ๋ง์ถฐ์ ธ ์์ผ๋ฉฐ, ์ธ๋ถ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ์ฌ์ด๋ ์ดํํธ(Side-Effect)๋ฅผ ์ต์ํํ๋ ๊ฒ์ด ์ข๋ค.
@Test
public void wordFreq1Test() {
List<String> words = Arrays.asList(
"stop", "spot", "trim", "meet",
"ball", "free", "trim", "meet"
);
Map<String, Long> freq = new HashMap<>();
// ๋ฌธ์ 1: ์ธ๋ถ ์ํ(freq)๋ฅผ ์์ ํ๋ ์คํธ๋ฆผ ์ฌ์ฉ
words.stream().forEach(
word -> freq.merge(word.toLowerCase(), 1L, Long::sum)
// ์ธ๋ถ ์ํ(freq)๋ฅผ ๋ณ๊ฒฝ -> ์คํธ๋ฆผ์ ํจ์ํ ํจ๋ฌ๋ค์์ ์๋ฐฐ
);
System.out.println("words = " + words);
System.out.println("freq = " + freq);
}- ์คํธ๋ฆผ์ forEach() ๋ฉ์๋๋ฅผ ์ด์ฉํ ์ฝ๋์ด๋ค.
- ๋ฐฐ์ด์ ์ฒ๋ฆฌํด์ผ ํ๋ค๋ฉด ์คํธ๋ฆผ์ผ๋ก ์ฒ๋ฆฌํ๊ฒ ๋ค๋ ์๊ฐ์์ ์ถ๋ฐํ์ ๊ฒ์ด๋ค.
๊ทธ๋ฐ๋ฐ, ์ผ๋ฐ์ ์ผ๋ก Stream ๋ฃจํ์์ ์ธ๋ถ ์ํ๋ฅผ ๋ณ๊ฒฝํ ๊ฒ์ด๋ผ๊ณค ์ฝ๊ฒ ์๊ฐํ์ง ๋ชปํ๋ค. ๊ทธ๋ฆฌ๊ณ , Stream ๋ฃจํ ๋ด๋ถ์์ Side-Effect ๊ฐ ์๋ ๋ด์ฉ์ ์์ฑํ๋ฉด ์ฌ๋ฌ๊ฐ์ง ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
๋ฌธ์ ๋ ์ธ๋ถ ์ํ์ธ ๋น๋ ์(freq)๋ฅผ ์์ ํ๋ ๋๋ค ๋ถ๋ถ์ด๋ค. ์ด ์ฝ๋์ ๋ชจ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์์ ์ด ์ต์ข ์ฐ์ฐ(์ข ๋จ ์ฐ์ฐ)์ธ forEach ๊ตฌ๋ฌธ์์ ์ผ์ด๋๊ณ ์๋๋ฐ, forEach๋ ์คํธ๋ฆผ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ํ ๋๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ๊ถ์ฅ๋๋ค. ์ฐ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ๋ ์ผ ์ด์์ ํ๋ ์ข์ ์ฝ๋๋ผ ํ ์ ์๋ค.
- ๊ฐ๋ ์ฑ: Stream ์ ์ฌ์ฉํ ์๊ฐ ๋ฐ์ดํฐ์ ๋ณํ๊ณผ ํ๊ฐ๊ฐ ์ด๋ค์ง ๊ฒ์ด๋ผ ๊ธฐ๋ํ๋๋ฐ ๊ทธ๋ฐ ์ฝ๋๊ฐ ์๋์ด์ ์ฝ๊ธฐ ์ด๋ ต๋ค.
- ์ฌ์ฌ์ฉ์ฑ: ์ธ๋ถ ์ํ์ ์์กดํด๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ฝ๊ฒ ์ฌ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํด์ง๋ค.
- ํ ์คํธ ๊ฐ๋ฅ์ฑ: Stream ์ ์ผ๋ฐ ๋ก์ง๊ณผ ๋ค๋ฅธ ํ๋ฆ์ ๊ฐ์ง๋ค. ๋ณ๋ ฌ๋ก๋ ์คํ๋ ์ ์๋ ๊ฒ์ด๋ผ ํ ์คํธํ๊ธฐ๋ ์ด๋ ค์์ง๋ค.
- ๋์์ฑ: ๋ฉํฐ ์ค๋ ๋ ํ๋ก๊ทธ๋๋ฐ์์ ํํ ๋ฐ์ํ๋ ๊ณต์ ๊ฐ๋ณ ์ํ์ ๊ด๋ จ๋ ๋ฌธ์ ๋ฅผ ํผํ๊ธฐ ์ด๋ ค์์ง๋ค.
์ผ๋ฐ์ ์ธ
for๋ฃจํ๋ฅผ ์ฌ์ฉํ์ฌ ๋จ์ด์ ๋น๋๋ฅผ ๊ณ์ฐํ๋ ์ฝ๋์ด๋ค. ์คํธ๋ฆผ์ ์ฌ์ฉ ์ฌ๋ถ์ ๋น๊ตํ์ ๋, ์คํธ๋ฆผ์ ์ต์ง๋ก ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๊ฐ๋ ์ฑ์ด๋ ํจ์จ์ฑ์ด ์คํ๋ ค ๋จ์ด์ง ์ ์๋ค.
- ์คํธ๋ฆผ์ ์ ์ ํ ์ฌ์ฉ
- ์คํธ๋ฆผ์ ๋ฐ์ดํฐ ๋ณํ๊ณผ ํ๊ฐ๋ฅผ ํจ์ํ ์คํ์ผ๋ก ๊ฐ๊ฒฐํ๊ฒ ํํํ ์ ์๋ค.
- ๊ทธ๋ฌ๋ ์คํธ๋ฆผ ๋ด๋ถ์์ ์ธ๋ถ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ์์ ์ ์คํธ๋ฆผ์ ์๋ ๋ชฉ์ ์ ๋ง์ง ์์ผ๋ฉฐ, ์ฝ๋์ ๋ช ํ์ฑ์ ํด์น๋ค.
- ์ฌ์ค ๋ง์ด ๋๋ฆฌ์ง ์์ผ๋ฉด for๋ฌธ๊ณผ ์ฑ๋ฅ์ ์ฐจ์ด๊ฐ ๋ณ๋ก ์๋ค.
- ์ผ๋ฐ for ๋ฃจํ์ ์ฅ์
- ์ผ๋ฐ์ ์ธ ์๋ฐ ๊ฐ๋ฐ์๋ค์๊ฒ ์น์ํ๋ฉฐ, ๋ช ํํ๊ณ ์ง๊ด์ ์ด๋ค.
- ๋ฐ์ดํฐ๋ฅผ ์ํํ๋ฉฐ ์ธ๋ถ ์ํ๋ฅผ ์์ ํ๋ ์์
์๋ ์คํธ๋ฆผ๋ณด๋ค
for๋ฃจํ๊ฐ ๋ ์ ํฉํ ์ ์๋ค. - ์คํธ๋ฆผ์
forEach๋ฅผ ์ฌ์ฉํ ์ฝ๋๋ณด๋ค ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ๋์ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
์คํธ๋ฆผ์ forEach๋ ๋ฐ์ดํฐ์ ์ต์ข
๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ํ ๋ ์ ํฉํ์ง๋ง, ๊ณ์ฐ ๊ทธ ์์ฒด๋ฅผ ์คํธ๋ฆผ ๋ด๋ถ์์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ์ ์ ํ์ง ์์ ์ ์๋ค. ์๋ฅผ ๋ค์ด:
์ด์ ์คํธ๋ฆผ ์ฝ๋ (์๋ชป๋ ์คํธ๋ฆผ ์ฌ์ฉ)
์ฌwords.stream().forEach(
word -> freq.merge(word.toLowerCase(), 1L, Long::sum)
);- ์ด ์ฝ๋๋ ์ธ๋ถ ์ํ(
freq)๋ฅผ ์์ ํ๋ ๋ถ์์ฉ(Side-Effect)์ด ๋ฐ์ํ๋ค. - ์คํธ๋ฆผ์ ์ฌ์ฉํ๋ ์ด์ (๋ฐ์ดํฐ ๋ณํ๊ณผ ํ๊ฐ)๊ฐ ์ฝํ๋์์ผ๋ฉฐ, ๋จ์ํ
forEach๋ฅผ ์ต์ง๋ก ์ฌ์ฉํ ๊ฒ์ ๋ถ๊ณผํ๋ค. - ์ผ๋ฐ for ๋ฃจํ๋ณด๋ค ๊ฐ๋ ์ฑ์ด ๋ฎ์์ง ์ ์๋ค.
์๋ ์ฝ๋๋ ์คํธ๋ฆผ์ ์ฌ์ฉํ์ง ์๊ณ ์ผ๋ฐ์ ์ธ for ๋ฃจํ๋ฅผ ์ฌ์ฉํ์ฌ ๋จ์ด์ ๋น๋๋ฅผ ๊ณ์ฐํ๋ ์ฝ๋
@Test
public void wordFreq1Test() {
List<String> words = Arrays.asList(
"stop", "spot", "trim", "meet",
"ball", "free", "trim", "meet"
);
Map<String, Long> freq = new HashMap<>();
// ์ผ๋ฐ์ ์ธ for ๋ฃจํ๋ฅผ ์ฌ์ฉ
for (String word : words) {
// ๋จ์ด๋ฅผ ์๋ฌธ์๋ก ๋ณํํ๊ณ ๋น๋์๋ฅผ ๊ฐฑ์
freq.merge(word.toLowerCase(), 1L, Long::sum);
}
System.out.println("words = " + words); // ์
๋ ฅ ๋จ์ด ๋ฆฌ์คํธ ์ถ๋ ฅ
System.out.println("freq = " + freq); // ๋จ์ด ๋น๋ ์ถ๋ ฅ
}- ์ธ๋ถ ์ํ๋ฅผ ์์ ํ๋ ์์
์๋
for๋ฃจํ๊ฐ ๋ ์ ํฉํ๋ค. - ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๊ณ ๋ คํ์ ๋, ์ต์ํ ์ฝ๋ ์คํ์ผ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค.
์คํธ๋ฆผ์ forEach ์ฝ๋๋ println() ๊ณผ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ด์ฉํด ๊ณ์ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ํ ๋๋ ์ ์ฉํ๋ค.๊ทธ๋ฌ๋ ๊ณ์ฐ ๊ทธ ์์ฒด์๋ ์คํ๋ ค ๋๋ฒ์งธ ์ฝ๋์ฒ๋ผ ์ผ๋ฐ์ ์ธ ์๋ฐ์ for ๋ฌธ์ ํ์ฉํ๋ ๊ฒ์ด ๋ ๊น๋ํ ๊ฒฝ์ฐ๋ ์๋ค.
import static java.util.stream.Collectors.*;
@Test
public void wordFreqCollectorTest() {
List<String> words = Arrays.asList(
"stop", "spot", "trim", "meet",
"ball", "free", "trim", "meet"
);
// Collectors๋ฅผ ์ฌ์ฉํด ๋จ์ด ๋น๋ ๊ณ์ฐ
// groupingBy: ๋จ์ด๋ฅผ ์๋ฌธ์๋ก ๊ทธ๋ฃนํ
// counting: ๊ฐ ๊ทธ๋ฃน์ ์์ ๊ฐ์ ๊ณ์ฐ
Map<String, Long> freq = words.stream()
.collect(groupingBy(String::toLowerCase, counting()));
System.out.println("freq = " + freq);
}
groupingBy์counting์ ์กฐํฉํ์ฌ ์คํธ๋ฆผ ๋ด๋ถ์์ ์ง์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ๊ฒฐ๊ณผ ์์ฑ์ ์ํํ๋ค.
freq = {stop=1, spot=1, trim=2, meet=2, ball=1, free=1}
- ์๋๊ฐ ๋ช ํ: ๋จ์ด๋ฅผ ์๋ฌธ์๋ก ๋ณํํ ๋ค ๊ทธ๋ฃนํํ์ฌ ๊ฐ ๊ทธ๋ฃน์ ๋น๋๋ฅผ ๊ณ์ฐํ๋ ์์ ์์ ์ฝ๋์์ ์ฝ๊ฒ ์ดํดํ ์ ์๋ค.
- ์ธ๋ถ ์ํ๋ฅผ ์์ ํ์ง ์์ผ๋ฏ๋ก ๋ณ๋ ฌ ์ฒ๋ฆฌ์์๋ ์์ ํ๋ค.
- ๋ฌธ์์ด์ toLowerCase() ๋ก ๋ณํํ์ฌ ๊ทธ๋ฃนํํ๊ฒ ๋ค๋ ์๋๊ฐ ๋ณด์ธ๋ค.
- ๊ทธ๋ฃนํ๋ key ์ ๋ํ value ๋ ๋จ์ด์ ๊ฐ์์ธ counting() ์ด ๋ค์ด๊ฐ ๊ฒ์ด๋ค.
- groupingBy() ๋ counting() ๊ฐ์ ๋ฉ์๋๋ฅผ ๊ฐ๋ ์ฑ ์ข๊ฒ ์ธ ์ ์๋ ์ด์ ๋ Collectors ์ ๋ฉค๋ฒ๋ฅผ ์ ์ ์ํฌํธํ๊ธฐ ๋๋ฌธ์ด๋ค.
forEach๋ ์คํธ๋ฆผ์ ์ต์ข ์ฐ์ฐ ์ค ํ๋๋ก, ์คํธ๋ฆผ์ ๊ฐ ์์๋ฅผ ์๋นํ๋ฉฐ ๋ฐ๋ณต์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค. ๊ทธ๋ฌ๋ ๊ธฐ๋ฅ์ด ์ ํ์ ์ด๊ณ , ์คํธ๋ฆผ์ ๋ณธ๋ ์๋์ธ ๋ณ๋ ฌ ์ฒ๋ฆฌ์๋ ๊ฑฐ๋ฆฌ๊ฐ ๋ฉ๋ฉฐ, ๊ฒฐ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ๋ฐ ์ ํฉํ์ง ์๋ค.- ์คํธ๋ฆผ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์์ฑํ๋ ์์
์๋
Collectors๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ ํฉํ๋ค. ํนํ, ๋ฐ์ดํฐ ๋ณํ์ด๋ ๊ทธ๋ฃนํ ์์ ์์Collectors๋ ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ์ ๊ณตํ๋ค.
- ๋ณ๋ ฌ ์ฒ๋ฆฌ ์ด๋ ค์:
forEach๋ ๋ณธ์ง์ ์ผ๋ก ๋ฐ๋ณต์ ์ด๊ธฐ ๋๋ฌธ์ ์คํธ๋ฆผ์ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ์ง ๋ชปํ๋ค.- ๋ฐ์ดํฐ๋ฅผ ๊ฒฐ๊ณผ ์ปฌ๋ ์ ์ผ๋ก ์์งํ๊ฑฐ๋ ๊ทธ๋ฃนํํ๋ ์์ ์ ์ ํฉํ์ง ์๋ค.
- ์๋ ์ ๋ฌ ๋ถ์กฑ:
forEach๋ ์คํธ๋ฆผ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ์ฉ๋๋ก ์ฌ์ฉ๋์ง๋ง, ๊ทธ ์์ฒด๋ก ๋ฐ์ดํฐ ๋ณํ์ด๋ ๊ทธ๋ฃนํ์ ์๋๋ฅผ ๋ช ํํ ์ ๋ฌํ์ง ๋ชปํ๋ค.
- ๊ฐ๋
์ฑ ์ ํ:
- ์คํธ๋ฆผ ์ฐ์ฐ์ ์ค๊ฐ ๋จ๊ณ์์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์์ฑํ๊ธฐ ์ํด ์ธ๋ถ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐฉ์์ ๊ฐ๋ ์ฑ์ ๋จ์ด๋จ๋ฆฌ๊ณ , ์ฝ๋์ ์ ์ง๋ณด์์ฑ์ ์ ํ์ํจ๋ค.
Java์ java.util.stream.Collectors ํด๋์ค๋ ์คํธ๋ฆผ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ํ ๊ฒฐ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ๋ฐ ํ์ํ ๋ค์ํ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค. ์ด๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋ฃนํํ๊ฑฐ๋ ์ง๊ณํ๋ฉฐ, ๋ช
ํํ๊ณ ๊ฐ๋
์ฑ ๋์ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
forEach ์ฐ์ฐ์ ์คํธ๋ฆผ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ํ ๋๋ง ์ฌ์ฉํ๊ณ , ๊ณ์ฐํ๋ ๋ฐ๋ ์ฐ์ง ๋ง์. ์์ง๊ธฐ๋ฅผ ์ฌ์ฉํ๋ฉด ์คํธ๋ฆผ์ ์์๋ฅผ ์์ฝ๊ฒ ์ปฌ๋ ์ ์ผ๋ก ๋ชจ์ ์ ์๋ค. ์์ง๊ธฐ๋ ์ด ์ธ ๊ฐ์ง๋ก, toListO, toSetO, toCollection(collectionFactory)๊ฐ ๊ทธ ์ฃผ์ธ๊ณต์ด๋ค.
@Test
public void topTwoWordsTest() {
// ๋จ์ด ๋ฆฌ์คํธ ์์ฑ
List<String> words = Arrays.asList("stop", "spot", "trim", "meet", "ball", "free", "trim", "trim", "meet");
// ๋จ์ด ๋น๋ ์ ๊ณ์ฐ
Map<String, Long> freq = words.stream()
.collect(groupingBy(String::toLowerCase, counting()));
// ๋จ์ด๋ฅผ ๋น๋ ์ ๊ธฐ์ค์ผ๋ก ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ ํ ์์ 2๊ฐ๋ง ์ถ์ถ
List<String> topTwo = freq.keySet() // Map์ keySet์ ์คํธ๋ฆผ์ผ๋ก ๋ณํ
.stream()
.sorted(Comparator.comparing(freq::get).reversed()) // ๋น๋ ์ ๊ธฐ์ค ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ
.limit(2) // ์์ 2๊ฐ๋ง ์ถ์ถ
.collect(Collectors.toList()); // ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ์คํธ๋ก ์์ง
// ๊ฒฐ๊ณผ ์ถ๋ ฅ
System.out.println("topTwo = " + topTwo); // ์์ 2๊ฐ์ ๋จ์ด ์ถ๋ ฅ
}topTwo = [trim, meet]
๋ง์ง๋ง toList๋ Collectors์ ๋ฉ์๋๋ค. ์ด์ฒ๋ผ Collectors์ ๋ฉค๋ฒ๋ฅผ ์ ์ ์ํฌํธํ์ฌ ์ฐ๋ฉด ์คํธ๋ฆผ ํ์ดํ๋ผ์ธ ๊ฐ๋ ์ฑ์ด ์ข์์ ธ, ํํ๋ค ์ด๋ ๊ฒ ์ฌ์ฉํ๋ค.
- ์ด ์ฝ๋์์ ์ด๋ ค์ด ๋ถ๋ถ์ sorted์ ๋๊ธด ๋น๊ต์, ์ฆ comparing(freq::get). reversed()๋ฟ์ด๋ค
comparing ๋ฉ์๋๋ ํค ์ถ์ถ ํจ์๋ฅผ ๋ฐ๋ ๋น๊ต์ ์์ฑ ๋ฉ์๋๋ค. ํ์ ์ ๋ฉ์๋ ์ฐธ์กฐ์ด์, ์ฌ๊ธฐ์ ํค ์ถ์ถ ํจ์๋ก ์ฐ์ธ freq::get์ ์ ๋ ฅ๋ฐ์ ๋จ์ด(ํค)๋ฅผ ๋น๋ํ์์ ์ฐพ์(์ถ์ถ) ๊ทธ ๋น๋๋ฅผ ๋ฐํํ๋ค.
Collectors๋ ์คํธ๋ฆผ ๋ฐ์ดํฐ๋ฅผ ํน์ ์ปฌ๋ ์ , ๋งต, ๋ฌธ์์ด ๋ฑ์ผ๋ก ๋ณํํ๊ฑฐ๋, ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋ฃนํํ๊ฑฐ๋, ์ง๊ณ ์์ ์ ์ํํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
| ๋ฉ์๋ | ์ค๋ช | ์์ |
|---|---|---|
toList() |
์คํธ๋ฆผ์ ๋ฐ์ดํฐ๋ฅผ ๋ฆฌ์คํธ๋ก ์์ง. | List<String> result = stream.collect(toList()); |
toSet() |
๋ฐ์ดํฐ๋ฅผ ์งํฉ(Set)์ผ๋ก ์์ง. | Set<String> result = stream.collect(toSet()); |
toMap() |
ํค์ ๊ฐ์ ๋งคํํ์ฌ ๋งต ์์ฑ. ํค ์ถฉ๋ ์ ๋ณํฉ ๋ก์ง์ ์ง์ ํ ์ ์์. | toMap(keyMapper, valueMapper, mergeFunction) |
groupingBy() |
๋ฐ์ดํฐ๋ฅผ ๊ทธ๋ฃนํํ์ฌ ๋งต ์์ฑ. | groupingBy(String::toLowerCase, counting()) |
partitioningBy() |
ํ๋ ๋์ผ์ดํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ฐ์ดํฐ๋ฅผ true/false ๋ ๊ทธ๋ฃน์ผ๋ก ๋๋. | partitioningBy(word -> word.equals("stop")) |
joining() |
๋ฌธ์์ด ์คํธ๋ฆผ์ ์ฐ๊ฒฐ. ๊ตฌ๋ถ์, ์ ๋์ฌ, ์ ๋ฏธ์ฌ ์ง์ ๊ฐ๋ฅ. | joining(", ", "๋ง์๋ ", "์ด ์ข์") |
| ๋ฉ์๋ | ์ค๋ช | ์์ |
|---|---|---|
counting() |
์์์ ๊ฐ์๋ฅผ ์ . | collect(counting()) |
summingInt() |
์์์ ํฉ๊ณ๋ฅผ ๊ตฌํจ. | collect(summingInt(Student::getScore)) |
averagingInt() |
์์์ ํ๊ท ์ ๊ตฌํจ. | collect(averagingInt(Student::getScore)) |
summarizingInt() |
ํฉ๊ณ, ํ๊ท , ์ต์, ์ต๋, ๊ฐ์๋ฅผ ํ ๋ฒ์ ํต๊ณ๋ก ๊ตฌํจ. | collect(summarizingInt(Student::getScore)) |
symbol๋ณ ์ฐ์ฐ์ ๊ธฐํธ ๋งคํํ๊ธฐ: ์ธ์ 2๊ฐ์ง๋ฆฌ toMap()
enum Operation {
PLUS("+", "plus", (x, y) -> x + y),
MINUS("-", "minus", (x, y) -> x - y),
TIMES("*", "times", (x, y) -> x * y),
DIVIDE("/", "divide", (x, y) -> x / y);
private final String symbol; // ์ฐ์ฐ์ ๊ธฐํธ
private final String english; // ์ฐ์ฐ์์ ์์ด ํํ
private final BinaryOperator<Double> op; // ์ฐ์ฐ์ ๋ก์ง
// ์์ฑ์
Operation(String symbol, String english, BinaryOperator<Double> op) {
this.symbol = symbol;
this.english = english;
this.op = op;
}
@Override
public String toString() {
return symbol; // ์ฐ์ฐ์ ๊ธฐํธ๋ฅผ ๋ฐํ
}
public String toEnglish() { return english; } // ์ฐ์ฐ์์ ์์ด ํํ ๋ฐํ
public double apply(double x, double y) {
return op.apply(x, y); // ์ฐ์ฐ์ ๋ก์ง ์คํ
}
}
@Test
public void stringToEnumTest() {
// Operation ์ด๊ฑฐํ์ ์์ด ํํ์ ํค, Operation ์์ฒด๋ฅผ ๊ฐ์ผ๋ก ๋งคํ
Map<String, Operation> operationMap = Stream.of(Operation.values())
.collect(toMap(e -> e.english, e -> e));
System.out.println("operationMap = " + operationMap);
// ๊ฒฐ๊ณผ: {minus=-, times=*, divide=/, plus=+}
}toMap์ ์ฌ์ฉํ์ฌenglish๋ฅผ ํค๋ก, ์ด๊ฑฐํ ์ธ์คํด์ค๋ฅผ ๊ฐ์ผ๋ก ๋งคํ.toMap(keyMapper, valueMapper)ํํ๋ก ์ฌ์ฉ๋๋ฉฐ, ํค์ ๊ฐ์ ๊ฐ๊ฐ ์ด๋ป๊ฒ ๋งคํํ ์ง ์ ์.- ๊ฒฐ๊ณผ:
{minus=-, times=*, divide=/, plus=+}
List ๋ฐ์ดํฐ Map<String, Album>์ผ๋ก merge()ํ๊ธฐ: ์ธ์ 3๊ฐ์ง๋ฆฌ toMap()
class Album {
private final String artist;
private final String name;
private final int sales;
public Album(String artist, String name, int sales) {
this.artist = artist;
this.name = name;
this.sales = sales;
}
public String artist() { return artist; }
public int sales() { return sales; }
@Override
public String toString() {
return "Album{name='" + name + "', sales=" + sales + "}";
}
}
@Test
public void bestAlbumPerArtist() {
// ์จ๋ฒ ๋ฐ์ดํฐ ์์ฑ
List<Album> albums = Arrays.asList(
new Album("Jake", "์ ์ดํฌ 1์ง", 100),
new Album("Jake", "์ ์ดํฌ 2์ง", 250),
new Album("Jack", "์ญ 1์ง", 990),
new Album("Jack", "์ญ 2์ง", 140)
);
// ์ํฐ์คํธ๋ณ ๋ฒ ์คํธ ์จ๋ฒ ์ฐพ๊ธฐ
Map<String, Album> topHits = albums.stream()
.collect(toMap(
Album::artist, // Key: ์ํฐ์คํธ ์ด๋ฆ
album -> album, // Value: ์จ๋ฒ ๊ฐ์ฒด
BinaryOperator.maxBy(Comparator.comparing(Album::sales)) // ๋ณํฉ: ์ต๊ณ ํ๋งค๋ ๊ธฐ์ค
));
System.out.println("topHits = " + topHits);
// ์ถ๋ ฅ: {Jake=Album{name='์ ์ดํฌ 2์ง', sales=250}, Jack=Album{name='์ญ 1์ง', sales=990}}
}toMap(keyMapper, valueMapper, mergeFunction)ํํ๋ฅผ ์ฌ์ฉ.- ๋ณํฉ ํจ์(
mergeFunction)๋กBinaryOperator.maxBy๋ฅผ ์ฌ์ฉํด ๊ฐ ์ํฐ์คํธ์ ํ๋งค๋์ด ๊ฐ์ฅ ๋ง์ ์จ๋ฒ์ ์ ํ. - ๊ฒฐ๊ณผ:
{Jake=Album{name='์ ์ดํฌ 2์ง', sales=250}, Jack=Album{name='์ญ 1์ง', sales=990}}
@Test
public void wordFreqGroupingTest() {
List<String> words = Arrays.asList("stop", "spot", "trim", "meet", "ball", "free", "triM", "TriM", "meet");
// ๋จ์ด๋ฅผ ์ํ๋ฒณ ์์ผ๋ก ์ ๋ ฌํ ๊ฒฐ๊ณผ๋ฅผ Key๋ก ์ฌ์ฉํ์ฌ ๊ทธ๋ฃนํ
Map<String, List<String>> alphabetizedMap = words.stream()
.collect(groupingBy(this::alphabetize));
System.out.println("alphabetizedMap = " + alphabetizedMap);
// ์ถ๋ ฅ: {eefr=[free], opst=[stop, spot], imrt=[trim], ...}
}
private String alphabetize(String s) {
char[] a = s.toCharArray();
Arrays.sort(a); // ๋จ์ด์ ๋ฌธ์๋ฅผ ์ ๋ ฌ
return new String(a); // ์ ๋ ฌ๋ ๋ฌธ์์ด ๋ฐํ
}groupingBy๋ฅผ ์ฌ์ฉํ์ฌ ์ํ๋ฒณ ์์ผ๋ก ์ ๋ ฌ๋ ๋จ์ด๋ฅผ ํค๋ก ๊ทธ๋ฃนํ.- ๊ฒฐ๊ณผ:
{eefr=[free], opst=[stop, spot], imrt=[trim], eemt=[meet, meet], ...}
@Test
public void studentScoreStatistics() {
List<Student> students = Arrays.asList(
new Student("Jake", 15),
new Student("Jackson", 55),
new Student("Joe", 85),
new Student("Amy", 21),
new Student("Roy", 33)
);
// ํ์ ์ ์ ํต๊ณ
IntSummaryStatistics stats = students.stream()
.collect(summarizingInt(Student::getScore));
System.out.println(stats);
// ์ถ๋ ฅ: IntSummaryStatistics{count=5, sum=209, min=15, average=41.800000, max=85}
}summarizingInt๋ฅผ ์ฌ์ฉํ์ฌ ํ์ ์ ์์ ํต๊ณ ์ ๋ณด๋ฅผ ํ ๋ฒ์ ์์ง.- ๊ฒฐ๊ณผ:
count,sum,min,average,max๊ฐ ํฌํจ๋IntSummaryStatistics๋ฐํ.
@Test
public void joiningTest() {
List<String> food = Arrays.asList("ํผ์", "ํ๋ฒ๊ฑฐ", "์นํจ");
// ๋ฌธ์์ด ์ฐ๊ฒฐ: ๊ตฌ๋ถ์, ์ ๋์ฌ, ์ ๋ฏธ์ฌ ์ง์
String result = food.stream()
.collect(joining(", ", "๋ง์๋ ", "์ด ์ข์"));
System.out.println(result); // ์ถ๋ ฅ: ๋ง์๋ ํผ์, ํ๋ฒ๊ฑฐ, ์นํจ์ด ์ข์
}joining(delimiter, prefix, suffix)๋ก ๋ฌธ์์ด ์ฐ๊ฒฐ.- ๊ฒฐ๊ณผ:
"๋ง์๋ ํผ์, ํ๋ฒ๊ฑฐ, ์นํจ์ด ์ข์"
- ๊ฐ๋
์ฑ:
groupingBy,toMap๋ฑ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์๋๋ฅผ ์ฝ๋์์ ๋ช ํํ ๋๋ฌ๋ธ๋ค.
- ํจ์จ์ฑ:
- ์คํธ๋ฆผ์ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ ์ ์์ผ๋ฉฐ, ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ ์ํํ๋ฉด์ ํ์ํ ๊ฒฐ๊ณผ๋ฅผ ์์ฑํ๋ค.
- ์ ์ฐ์ฑ:
- ๋ค์ํ ์์ง๊ธฐ ์กฐํฉ์ ํตํด ๋ณต์กํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ ๊ฐ๊ฒฐํ๊ฒ ํํํ ์ ์๋ค.
toMap์ฌ์ฉ ์ ์ฃผ์์ :- ํค๊ฐ ์ค๋ณต๋ ๊ฒฝ์ฐ
IllegalStateException์ด ๋ฐ์ํ๋ฏ๋ก, ๋ณํฉ ํจ์(mergeFunction)๋ฅผ ์ ๊ณตํด์ผํ๋ค.
- ํค๊ฐ ์ค๋ณต๋ ๊ฒฝ์ฐ
groupingBy์partitioningBy์ฐจ์ด:groupingBy๋ ์ฌ๋ฌ ์นดํ ๊ณ ๋ฆฌ๋ก ๊ทธ๋ฃนํ.partitioningBy๋ true/false ๋ ๊ทธ๋ฃน์ผ๋ก ๋ถ๋ฅ.
joiningํ์ฉ:- ๊ตฌ๋ถ์, ์ ๋์ฌ, ์ ๋ฏธ์ฌ๋ฅผ ์ง์ ํ์ฌ ๋ฌธ์์ด์ ์ฝ๊ฒ ์ฐ๊ฒฐ ๊ฐ๋ฅ.
- ๋ณ๋ ฌ ์ฒ๋ฆฌ:
groupingByConcurrent,toConcurrentMap์ ์ฌ์ฉํ๋ฉด ๋ณ๋ ฌ ์คํธ๋ฆผ ๊ฒฐ๊ณผ๋ฅผConcurrentHashMap์ ์ ์ฅํ ์ ์๋ค.
forEach์ ํ๊ณ
forEach๋ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ๊ฑฐ๋, ๋ณด๊ณ ๋ชฉ์ ์ผ๋ก๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.- ๋ฐ์ดํฐ ๋ณํ์ด๋ ์ง๊ณ ์์
์
Collectors๋ฅผ ํ์ฉํด์ผ ๊ฐ๋ ์ฑ๊ณผ ํจ์จ์ฑ์ ๋์ผ ์ ์๋ค.
์คํธ๋ฆผ ํ์ฉ ์์น
- ์๋๋ฅผ ๋ช
ํํ ํํ:
- ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๊ฑฐ๋ ์ง๊ณํ ๋ ์คํธ๋ฆผ์ ๊ฐ ๋จ๊ณ๊ฐ ์ํํ๋ ์์ ์ ๋ช ํํ ๋ณด์ฌ์ผ ํ๋ค.
- ํจ์ํ ์คํ์ผ ์ ์ง:
- ์ธ๋ถ ์ํ๋ฅผ ์์ ํ๋ ์ฌ์ด๋ ์ดํํธ๋ฅผ ํผํ๊ณ , ์คํธ๋ฆผ ๋ด๋ถ์์ ์์ ์ ์๋ฃํ๋๋ก ์ค๊ณํ๋ค.
Collectors๋ ์คํธ๋ฆผ API์ ๊ฒฐํฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์์ง, ๊ทธ๋ฃนํ, ์ง๊ณํ๋ ๋ฐ ์์ด ํ์์ ์ธ ๋๊ตฌ์ด๋ค. ์ด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๋ ค๋ฉด ๋ค์ํ ์์ง๊ธฐ ๋ฉ์๋๋ฅผ ์ดํดํ๊ณ ์ ์ฌ์ ์์ ํ์ฉํด์ผ ํ๋ค. ์คํธ๋ฆผ์ ๋ณธ์ง์ ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์์ฑํ๋ ๊ฒ์ด๋ฉฐ, ์ด๋ฅผ ํตํด ๋ช ํํ๊ณ ๊ฐ๋ ์ฑ ๋์ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
- ์คํธ๋ฆผ ํ์ดํ๋ผ์ธ ํ๋ก๊ทธ๋๋ฐ์ ํต์ฌ์ ๋ถ์์ฉ ์๋ ํจ์ ๊ฐ์ฒด์ ์๋ค.
- ์คํธ๋ฆผ๋ฟ ์๋๋ผ ์คํธ๋ฆผ ๊ด๋ จ ๊ฐ์ฒด์ ๊ฑด๋ค์ง๋ ๋ชจ๋ ํจ์ ๊ฐ์ฒด๊ฐ ๋ถ์์ฉ์ด ์์ด์ผ ํ๋ค.
- ์ข ๋จ ์ฐ์ฐ ์ค forEach๋ ์คํธ๋ฆผ์ด ์ํํ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ํ ๋๋ง ์ด์ฉํด์ผ ํ๋ค. ๊ณ์ฐ ์์ฒด์๋ ์ด์ฉํ์ง ๋ง์.
- ์คํธ๋ฆผ์ ์ฌ๋ฐ๋ก ์ฌ์ฉํ๋ ค๋ฉด ์์ง๊ธฐ๋ฅผ ์ ์์๋ฌ์ผ ํ๋ค. ๊ฐ์ฅ ์ค์ํ ์์ง๊ธฐ ํฉํฐ๋ฆฌ๋ toList, toSet, toHap, groupingBy, joining์ด๋ค
์ถ์ฒ ๋ฐ ์ฐธ๊ณ
 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png)