DevLog ๐ถ
[๋ชจ๋์๋ฐ์ธ์ก์ ] ์คํธ๋ฆผ์ด๋ ๋ฌด์์ผ๊น? ๋ณธ๋ฌธ
[๋ชจ๋์๋ฐ์ธ์ก์ ] ์คํธ๋ฆผ์ด๋ ๋ฌด์์ผ๊น?
dolmeng2 2023. 4. 7. 11:49๐ฌ ๋ชจ๋ ์๋ฐ ์ธ ์ก์ ์ฑํฐ 4์ ์ฝ๊ณ ์ ๋ฆฌํ ๊ธ์ ๋๋ค.
โ๏ธ ์คํธ๋ฆผ์ด๋?
์๋ฐ 8์ ์๋กญ๊ฒ ๋ค์ด์จ ๊ธฐ๋ฅ ์ค, ๊ฐ์ฅ ํต์ฌ์ ์ธ ๊ธฐ๋ฅ์ด๋ผ๊ณ ๋งํด๋ ๋ฌด๋ฐฉํ๋ค.
์ปฌ๋ ์ ๋ฐ์ดํฐ์ ๋ฐ๋ณต์ ๋์์ฃผ๊ณ , ๋ฉํฐ ์ค๋ ๋ ์ฝ๋ ์์ด ๋ณ๋ ฌ์ฑ์ ๊ตฌํํ ์ ์๋ ์์ฃผ ์ ์ฉํ ์น๊ตฌ์ด๋ค.
๋ํ, filter๋ sorted, map, collect ๊ฐ์ ์ฐ์ฐ์ ‘high-level building block’์ด๋ผ๊ณ ํ๋๋ฐ, ์คํธ๋ฆผ์ ๋ง์น ๋ธ๋ก์ฒ๋ผ ์๊ณ , ์กฐ๋ฆฝํ ์ ์๋ค๋ ์ ์ด ํฐ ํน์ง์ด๋ค.
โ๏ธ stream ๋ฉ์๋
๐ฑ ์คํธ๋ฆผ์ด๋, ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ฐ์ฐ์ ์ํด ์์ค์์ ์ถ์ถ๋ ์ฐ์๋ ์์์ด๋ค.
1) ์ฐ์๋ ์์
์ปฌ๋ ์ ์ ๋ฐ์ดํฐ์ ๋ํ ์๋ฃ๊ตฌ์กฐ์๋ค๋ฉด, ์คํธ๋ฆผ์ ์ด๋ฅผ ์ด๋ค ์์ผ๋ก ๊ณ์ฐํ ์ง์ ๋ํด ๋ค๋ฃฌ๋ค.
๋ ๊ฐ์ง ๋ชจ๋, ์ฐ์๋ ๊ฐ ์งํฉ์ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
2) ์์ค
๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํด์ฃผ๋ ์์ค๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์๋นํ๋ค.
์คํธ๋ฆผ์๊ฒ ์ ๊ณตํ ๋ฆฌ์คํธ๊ฐ ์ ๋ ฌ๋์๋ค๋ฉด ํด๋น ์คํธ๋ฆผ์์๋ ๋ฆฌ์คํธ๋ ์ ๋ ฌ๋์ด ์๋ค.
3) ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ฐ์ฐ
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ์ฐ์ฐ ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ์ฐ๊ณผ ๋น์ทํ ์ฐ์ฐ์ ์ ๊ณตํ๋ฉฐ, ์์ฐจ/๋ณ๋ ฌ ์คํ์ด ๊ฐ๋ฅํ๋ค.
๋ํ, ์คํธ๋ฆผ์ ์๊ธฐ ์์ ์ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์ฒด์ด๋์ด ๊ฐ๋ฅํ๋ฉฐ, ๋ฐ๋ณต์๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๋ด๋ถ ๋ฐ๋ณต์ ํตํด ๋ฐ๋ณต์ ์งํํ๋ค.
๊ธ๋ก ๋ณด๋ฉด ์ดํดํ๊ธฐ ์ด๋ ต๊ธฐ ๋๋ฌธ์, ์ฝ๋๋ก ์ดํด๋ณด์.
// course
public enum Course {
BACKEND, FRONTEND, ANDROID
}
// crew
public class Crew {
private final String name;
private final String nickname;
private final int age;
private final Course course;
public Crew(final String name, final String nickname, final int age, final Course course) {
this.name = name;
this.nickname = nickname;
this.age = age;
this.course = course;
}
}
์ฐ๋ฆฌ๋ ๋ฐฑ์๋ ๊ณผ์ ์ธ ํฌ๋ฃจ๋ฅผ ์ต๋ 10๋ช ๋ฝ์ ํด๋น ํฌ๋ฃจ์ ์ด๋ฆ์ ๊ฐ์ ธ์ฌ ๊ฒ์ด๋ค.
๋ง์น, DB์ ์ฟผ๋ฆฌ๋ฅผ ์ง์๋ฅผ ํ๋ ๊ฒ๊ณผ ๊ฐ์ ์กฐ๊ฑด์ด๋ค.
์คํธ๋ฆผ์ ํ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํํํ ์ ์๋ค.
List<String> backendCrews = crews.stream()
// ๋๋ค์ ์ธ์(๋ฐฑ์๋ ํฌ๋ฃจ)๋ฅผ ๋ฐ์ ํน์ ์์๋ฅผ ์ ํํ๋ค.
.filter(crew -> crew.getCourse() == Course.BACKEND)
// ๋๋ค๋ฅผ ์ด์ฉํ์ฌ ํ๋์ ์์๋ฅผ ๋ค๋ฅธ ์์๋ก ๋ฐํํ๋ค.
.map(Crew::getName)
// ์คํธ๋ฆผ ํฌ๊ธฐ์ ์ต๋ ์ฌ์ด์ฆ๋ฅผ ์ง์ ํ๋ค.
.limit(10)
// ์คํธ๋ฆผ์ ๋ค๋ฅธ ํ์์ผ๋ก ๋ณํํ๋ค. (์ฌ๊ธฐ์๋ ๋ฆฌ์คํธ๋ก ๋ณํ)
.collect(Collectors.toUnmodifiableList());
์ฌ๊ธฐ์ ๋ฐ์ดํฐ ์์ค๋, ํฌ๋ฃจ ๋ฆฌ์คํธ๋ฅผ ์๋ฏธํ๋ฉฐ, ๊ฐ๊ฐ์ ํฌ๋ฃจ์ ๋ํ ‘์ฐ์๋ ์์’๋ฅผ ์คํธ๋ฆผ์๊ฒ ์ ๊ณตํ๋ค.
filter-map-limit-collect๋ฅผ ํตํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ฐ์ฐ์ ์งํํ๋ฉฐ, filter-map-limit์ ๊ฒฝ์ฐ ๋ฐํํ์ด Stream์ด๊ธฐ ๋๋ฌธ์ ์๋ก ํ์ดํ๋ผ์ธ์ ํ์ฑํ๊ฒ ๋๋ค. (์ฒด์ด๋์ด ๊ฐ๋ฅํด์ง๋ค!)
โ๏ธ ์คํธ๋ฆผ vs ์ปฌ๋ ์
๋ ๊ฐ์ง ๋ชจ๋ ์ฐ์๋ ์์์ ๊ฐ์ ์ ์ฅํ๋ ์๋ฃ๊ตฌ์กฐ์ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
๐ฑ ์ฐ์ฐ ์ธก๋ฉด์์์ ์ฐจ์ด
์ปฌ๋ ์ ์ ํด๋น ์๋ฃ๊ตฌ์กฐ๊ฐ ํฌํจํ๋ ๋ชจ๋ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์, ์ปฌ๋ ์ ์ ์ถ๊ฐ๋ ์ญ์ ๋ฑ์ ์ฐ์ฐ์ด ์ฃผ์ด์ง๋ค๋ฉด ํด๋น ์์๋ค์ ๋ํ ์ ๋ณด๋ฅผ ๋ชจ๋ ์๊ณ ์์ด์ผ ํ๋ค. (ex. ๋งํ์ฑ )
ํ์ง๋ง, ์คํธ๋ฆผ์ ์์ฒญ ์์๋ง ํด๋น ์์๋ฅผ ๊ณ์ฐํ๋ค. (์คํธ๋ฆผ ์์ฒด์ ์์๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์ ๊ฑฐํ ์๊ฐ ์๋ค)
๋ํ, ์์ฐ์-์๋น์ ๊ด๊ณ๋ก ์ธํด์ ์ฌ์ฉ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ ๋๋ง ๊ฐ์ ์์ฑํ์ฌ ๋ฐํํ๋ค. (ex. ์ค์๊ฐ์ผ๋ก ๋ก๋ ์ค์ธ ์นํฐ)
๐ฑ ์คํธ๋ฆผ์ ํ ๋ฒ๋ง ํ์์ด ๊ฐ๋ฅํ๋ค
์คํธ๋ฆผ์ ๋จ ํ ๋ฒ๋ง ํ์์ด ๊ฐ๋ฅํ๋ฉฐ, ํ์๋ ์์๋ ์๋น๋๋ค. ์ฌํ์์ ์ํด์๋ ๋ค์ ์๋ก์ด ์คํธ๋ฆผ์ ๋ง๋ค์ด์ผ ํ๋ค.
List<Crew> crews = new ArrayList<>();
Stream<String> backendCrews = crews.stream()
.filter(crew -> crew.getCourse() == Course.BACKEND)
.map(Crew::getName)
.limit(10);
backendCrews.forEach(System.out::println);
backendCrews.forEach(System.out::println); // Error!
Intellij์์๋ ์น์ ํ๊ฒ๋ ์คํธ๋ฆผ์ ์ฌ์ฌ์ฉํ๋ ค๊ณ ํ๋ ค๋ฉด ์์ ๊ฐ์ด ์ด๋ฏธ ์๋น๋์๋ค๊ณ ๋ง์ ํด์ค๋ค.
๐ฑ ์ธ๋ถ ๋ฐ๋ณต๊ณผ ๋ด๋ถ ๋ฐ๋ณต
๋ํ, ๋ฐ๋ณต์ ๋ํด์๋ ๋์ ์ฐจ์ด๊ฐ ์๋ค.
์ปฌ๋ ์ ์ ์ฌ์ฉ์๊ฐ ์ง์ ๋ฐ๋ณต์ ํด์ผ ํ์ง๋ง (์ธ๋ถ ๋ฐ๋ณต), ์คํธ๋ฆผ์ ๋ฐ๋ณต์ ๋ด๋ถ์ ์ผ๋ก ์์์ ์ฒ๋ฆฌํ๊ณ ์ด๋๊ฐ์ ์ ์ฅํ๋ค. (๋ด๋ถ ๋ฐ๋ณต)
์ธ๋ถ ๋ฐ๋ณต์ “๋ช ์์ ์ผ๋ก” ๋ด๋ถ ์์๋ค์ ํ๋์ฉ ์ ๊ทผํ์ฌ, ๊ฐ์ ธ์์ ์ฒ๋ฆฌํ๋ค.
์ฆ, ํฌ๋ฃจ ๋ฆฌ์คํธ๊ฐ ์๋ค๋ฉด ํฌ๋ฃจA, ํฌ๋ฃจB, ํฌ๋ฃจC… ํฌ๋ฃจZ๊น์ง, ๋ชจ๋ ํฌ๋ฃจ๋ค์ ๋ํด์ ํ๋์ฉ ๊ฐ์ ธ์์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด๋ค.
: ๊ฐ๋ฐ์๊ฐ ๋ช ์์ ์ผ๋ก ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ์ฌ, ๋ชจ๋ ์์๋ฅผ ํ๋์ฉ ๊ฐ์ ธ์์ ์ฒ๋ฆฌ.
// Iterator ์ฌ์ฉ
List<String> names = new ArrayList<>();
Iterator<String> iterator = crews.iterator();
while(iterator.hasNext()) {
Crew crew = iterator.next();
names.add(dish.getName());
}
// for-each ์ฌ์ฉ
List<String> names = new ArrayList<>();
for (Crew crew : crews) {
names.add(crew.getName());
}
์์ ์ฝ๋๋ฅผ ๋ณด์. ๋ ์ฝ๋ ๋ชจ๋ ์ง์ ์ ์ผ๋ก ๊ฐ๋ฐ์๊ฐ ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ์ฌ, ๊ฐ ์์์ ๋ํด์ ์ฒ๋ฆฌํ๊ณ ์๋ค.
๊ทธ๋ฌ๋, ๋ด๋ถ ๋ฐ๋ณต์ด๋ผ๋ฉด ํฌ๋ฃจ๋ฅผ ๊ฐ์ ธ์ค๋ฉด์ ๋ค๋ฅธ ํ์๋ ํ ์ ์๊ณ (map, filter...),
์์ฐจ์ ์ผ๋ก ๊ฐ์ ธ์ค์ง ์๊ณ ํฌ๋ฃจA, ํฌ๋ฃจC… ์ด๋ฐ ์์ผ๋ก ๋ณ๋ ฌ์ ์ผ๋ก๋ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค.
: ๊ฐ๋ฐ์๋ ๋จ์ํ ์ฒ๋ฆฌ ๊ณผ์ ๋ง ์ ์ํ๊ณ , ๋ด๋ถ์ ์ผ๋ก ์คํธ๋ฆผ์ด ์์๋ฅผ ๋ฐ๋ณตํ๋ฉฐ ์ฒ๋ฆฌ.
Stream<String> backendCrews = crews.stream()
.map(Crew::getName)
.limit(10);
์์ ์ฝ๋๋ฅผ ๋ณด์. map์ ํตํด ํฌ๋ฃจ๋ค์ ์ด๋ฆ์ ์ฐ์ถํ๊ณ ์๋ค.
์ฌ๊ธฐ์ ๊ฐ๋ฐ์๋ 'map์ ํตํด ํฌ๋ฃจ๋ค์ ์ด๋ฆ์ผ๋ก ๋งคํ์ํค๋ ํ์'๋ง์ ์ง์ ํด์ฃผ๊ณ ์์ผ๋ฉฐ, ์คํธ๋ฆผ์ ๋ด๋ถ์ ์ผ๋ก crew ๋ฆฌ์คํธ๋ฅผ ๋๋ฉด์ ๋งคํํด์ฃผ๋ ์์ ์ ์งํํ ๊ฒ์ด๋ค. ๋ณ๋์ ๋ฐ๋ณต์๊ฐ ํ์ํ์ง ์๋ ๊ฒ์ด๋ค.
โ๏ธ ์ค๊ฐ ์ฐ์ฐ๊ณผ ์ต์ข ์ฐ์ฐ
์คํธ๋ฆผ์ ์ค๊ฐ ์ฐ์ฐ๊ณผ ์ต์ข ์ฐ์ฐ์ผ๋ก ๋๋๋ค.
๐ฑ ์ค๊ฐ ์ฐ์ฐ
์ฐ์ฐ | ๋ฐํ ํ์ | ์ฐ์ฐ์ ์ธ์ | ํจ์ ๋์คํฌ๋ฆฝํฐ |
filter | Stream<T> | Predicate<T> | T -> boolean |
map | Stream<R> | Function<T, R> | T -> R |
limit | Stream<T> | ||
sorted | Stream<T> | Comparator<T> | (T, T) -> int |
distinct | Stream<T> |
๐ฑ ์ต์ข ์ฐ์ฐ
์ฐ์ฐ | ๋ฐํ ํ์ | ๋ชฉ์ |
forEach | void | ์คํธ๋ฆผ์ ๊ฐ ์์๋ฅผ ์๋นํ๋ฉด์ ๋๋ค๋ฅผ ์ ์ฉํ๋ค. |
count | long (generic) | ์คํธ๋ฆผ์ ์์ ๊ฐ์๋ฅผ ๋ฐํํ๋ค. |
collect | ์คํธ๋ฆผ์ ๋ฆฌ๋์คํด์ ๋ฆฌ์คํธ, ๋งต, ์ ์ ํ์์ ์ปฌ๋ ์ ์ ๋ง๋ ๋ค. |
์ค๊ฐ ์ฐ์ฐ์ ์ด์ฉํ๋ฉด ๋ฐํํ์ด Stream์ด๊ธฐ ๋๋ฌธ์ ๊ณ์ ์ฒด์ด๋์ ํตํด์ ์ฐ๊ฒฐํ ์ ์์ผ๋ฉฐ, ์ต์ข ์ฐ์ฐ์ ์คํํ๊ธฐ ์ ๊น์ง๋ ์๋ฌด ์ฐ์ฐ๋ ์คํํ์ง ์๋๋ค๋ ์ ์ด ํน์ง์ด๋ค. (์ฑ ์์๋ ๊ฒ์ผ๋ฅด๋ค๊ณ ํํํ๋ค)
๊ทธ๋ฆฌ๊ณ , ์ต์ข ์ฐ์ฐ์ ์ด์ฉํ๋ฉด ์คํธ๋ฆผ ํ์ดํ๋ผ์ธ์์ ๊ฒฐ๊ณผ๋ฅผ ๋์ถํ๊ฒ ๋๋ฉฐ, Streamํ์ด ์๋ List(Collectors.toUnmodifiableList()), void(forEach), long(count) ๋ฑ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ก ๋ฐํํ ์ ์๋ค.
List<Crew> crews = List.of(Crew.createByName("์ด๋ฆ1", 20, Course.BACKEND),
Crew.createByName("์ด๋ฆ2", 21, Course.BACKEND),
Crew.createByName("์ด๋ฆ3", 22, Course.BACKEND),
Crew.createByName("์ด๋ฆ4", 23, Course.BACKEND));
์์๋ฅผ ๋ค์ด๋ณด์. ํฌ๋ฃจ์ ๋ํ ๋ฆฌ์คํธ๋ฅผ ์ ์ํด๋ ๊ฒ์ด๋ค.
Stream<String> names =
crews.stream()
.filter(crew -> {
System.out.println("filter: " + crew.getName());
return crew.getAge() > 20;
})
.map(crew -> {
System.out.println("map: " + crew.getName());
return crew.getName();
})
.limit(2);
์ด๋, ์์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๊ฒ ๋๋ฉด (์ค๊ฐ ์ฐ์ฐ์ ๋ชจ์) ์ด๋ค ๊ฒฐ๊ณผ๊ฐ ๋์ฌ๊น?
๋๋๊ฒ๋ ์๋ฌด๊ฒ๋ ์ถ๋ ฅ๋์ง ์๋๋ค.
์คํธ๋ฆผ์ ์ต์ข ์ฐ์ฐ์ด ์ํ๋๊ธฐ ์ ๊น์ง, ์๋ฌด๊ฒ๋ ํ์ง ์๋๋ค๋ ๊ฒ ์ด๋ฐ ์๋ฏธ์ด๋ค.
List<String> names =
crews.stream()
.filter(crew -> {
System.out.println("filter: " + crew.getName());
return crew.getAge() > 20;
})
.map(crew -> {
System.out.println("map: " + crew.getName());
return crew.getName();
})
.limit(2)
.collect(toList());
System.out.println("names = " + names);
๋ฐ๋ฉด์, collect๋ฅผ ํตํด ์ต์ข ์ฐ์ฐ์ ์ํํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
์คํํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋์จ๋ค.
์ต์ข ์ฐ์ฐ์ด ์ํ๋์๊ธฐ ๋๋ฌธ์ ์ค๊ฐ ์ฐ์ฐ์ด ์คํ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ํ, limit๋ฅผ ํตํด ์ต๋ 2๊ฐ๊น์ง๋ง ๊ฐ์ ธ์ค๋๋ก ์ค์ ์ ํ๊ธฐ ๋๋ฌธ์, ์ฒ์๋ถํฐ ์กฐ๊ฑด์ '์ด๋ฆ4'๋ ๋ค์ด๊ฐ์ง ์์ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ด๋ฅผ '์ผํธ ์ํท' ์ฐ์ฐ์ด๋ผ๊ณ ํ ์ ์๋๋ฐ, ๋ชจ๋ ์์๋ฅผ ์ฒ๋ฆฌํ์ง ์๊ณ ์ฃผ์ด์ง ํฌ๊ธฐ์ ์คํธ๋ฆผ์ ์์ฑํ์ฌ ๊ทธ๋งํผ๋ง ์ฒ๋ฆฌํ๋ ๊ฒ์ด๋ค.
์คํธ๋ฆผ์ ๋ํด ๋ค์๊ณผ ๊ฐ์ด ์์ฝํ๋ฉฐ ๋ง๋ฌด๋ฆฌํ๊ณ ์ ํ๋ค.
1. ์ง์๋ฅผ ์ํํ ๋ฐ์ดํฐ ์์ค
2. ์คํธ๋ฆผ ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑํ ์ค๊ฐ ์ฐ์ฐ ์ฐ๊ฒฐํ๊ธฐ
3. ์คํธ๋ฆผ ํ์ดํ๋ผ์ธ์ ์คํํ๊ณ , ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค๊ธฐ ์ํ ์ต์ข ์ฐ์ฐ์ ํตํด ๋์ถํ๊ธฐ