item 45 dodo4513 - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

์•„์ดํ…œ45 ์ŠคํŠธ๋ฆผ์€ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ผ

์ŠคํŠธ๋ฆผ์ด๋ž€?

  • ์ŠคํŠธ๋ฆผ API๋Š” ๋‹ค๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์ž‘์—…(์ˆœ์ฐจ์ ์ด๋“  ๋ณ‘๋ ฌ์ ์ด๋“ )์„ ๋•๊ณ ์ž ์ž๋ฐ” 8์— ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.
  • ์ด API๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ถ”์ƒ ๊ฐœ๋… ์ค‘ ํ•ต์‹ฌ์€ ๋‘ ๊ฐ€์ง€๋‹ค.
    1. ๊ทธ ์ฒซ ๋ฒˆ์งธ์ธ ์ŠคํŠธ๋ฆผ(stream)์€ ๋ฐ์ดํ„ฐ ์›์†Œ์˜ ์œ ํ•œ ํ˜น์€ ๋ฌดํ•œ ์‹œํ€€์Šค(sequence)๋ฅผ ๋œปํ•œ๋‹ค.
    2. ๋‘ ๋ฒˆ์งธ์ธ ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ(stream pipeline)์€ ์ด ์›์†Œ๋“ค๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ์—ฐ์‚ฐ ๋‹จ๊ณ„๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๊ฐœ๋…์ด๋‹ค.

ํŠน์ง•

1. ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ

  • ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์€ ์†Œ์Šค ์ŠคํŠธ๋ฆผ์—์„œ ์‹œ์ž‘ํ•ด ์ข…๋‹จ ์—ฐ์‚ฐ(terminal operation)์œผ๋กœ ๋๋‚˜๋ฉฐ, ๊ทธ ์‚ฌ์ด์— ํ•˜๋‚˜ ์ด์ƒ์˜ ์ค‘๊ฐ„ ์—ฐ์‚ฐ(intermediate operation)์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์€ ์ŠคํŠธ๋ฆผ์„ ์–ด๋– ํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ณ€ํ™˜(transform)ํ•œ๋‹ค.
  • ์ข…๋‹จ ์—ฐ์‚ฐ์€ ๋งˆ์ง€๋ง‰ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์ด ๋‚ด๋†“์€ ์ŠคํŠธ๋ฆผ์— ์ตœํ›„์˜ ์—ฐ์‚ฐ์„ ๊ฐ€ํ•œ๋‹ค.

2. ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์€ ์ง€์—ฐ ํ‰๊ฐ€(lazy evaluation)๋œ๋‹ค.

  • ํ‰๊ฐ€๋Š” ์ข…๋‹จ ์—ฐ์‚ฐ์ด ํ˜ธ์ถœ๋  ๋•Œ ์ด๋ค„์ง€๋ฉฐ, ์ข…๋‹จ ์—ฐ์‚ฐ์— ์“ฐ์ด์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ ์›์†Œ๋Š” ๊ณ„์‚ฐ์— ์“ฐ์ด์ง€ ์•Š๋Š”๋‹ค.
  • ์ด๋Ÿฌํ•œ ์ง€์—ฐ ํ‰๊ฐ€๊ฐ€ ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์—ด์‡ ๋‹ค.
  • ์ข…๋‹จ ์—ฐ์‚ฐ์ด ์—†๋Š” ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์€ ์•„๋ฌด ์ผ๋„ ํ•˜์ง€ ์•Š๋Š” ๋ช…๋ น์–ด์ธ no-op๊ณผ ๊ฐ™์œผ๋‹ˆ, ์ข…๋‹จ ์—ฐ์‚ฐ์„ ๋นผ๋จน๋Š” ์ผ์ด ์ ˆ๋Œ€ ์—†๋„๋ก ํ•˜์ž.

3. ๊ธฐํƒ€ ๋“ฑ๋“ฑ

  • ์ŠคํŠธ๋ฆผ API๋Š” ๋ฉ”์„œ๋“œ ์—ฐ์‡„๋ฅผ ์ง€์›ํ•˜๋Š” ํ”Œ๋ฃจ์–ธํŠธ API(fluent API)๋‹ค. ์ฆ‰, ํŒŒ์ดํ”„๋ผ์ธ ํ•˜๋‚˜๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ชจ๋“  ํ˜ธ์ถœ์„ ์—ฐ๊ฒฐํ•˜์—ฌ ๋‹จ ํ•˜๋‚˜์˜ ํ‘œํ˜„์‹์œผ๋กœ ์™„์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค
  • ๊ธฐ๋ณธ์ ์œผ๋กœ ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์€ ์ˆœ์ฐจ์ ์œผ๋กœ ์ˆ˜ํ–‰๋œ๋‹ค. ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•˜๋ ค๋ฉด ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•˜๋Š” ์ŠคํŠธ๋ฆผ ์ค‘ ํ•˜๋‚˜์—์„œ parallel ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๋‚˜, ํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์€ ๋งŽ์ง€ ์•Š๋‹ค
  • ์ŠคํŠธ๋ฆผ API๋Š” ๋‹ค์žฌ๋‹ค๋Šฅํ•˜์—ฌ ์‚ฌ์‹ค์ƒ ์–ด๋– ํ•œ ๊ณ„์‚ฐ์ด๋ผ๋„ ํ•ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด์ง€, ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋œป์€ ์•„๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์„ ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์ด ์งง๊ณ  ๊น”๋”ํ•ด์ง€์ง€๋งŒ, ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ์ฝ๊ธฐ ์–ด๋ ต๊ณ  ์œ ์ง€๋ณด์ˆ˜๋„ ํž˜๋“ค์–ด ์ง„๋‹ค.

์ŠคํŠธ๋ฆผ์„ ๋‚จ์šฉํ•˜๋Š” ๊ฒฝ์šฐ?

// case1: ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์ฝ”๋“œ

public class Anagrams {
    public static void main(String[] args) throws IOException {
        File dictionary = new File(args[0]);
        int minGroupSize = Integer.parseInt(args[1]);
        Map<String, Set<String>> groups = new HashMap<>();
        try (Scanner s = new Scanner(dictionary)) {
            while (s.hasNext()) {
                String word = s.next();
                groups.computeIfAbsent(alphabetize(word),
                                       (unused) -> new TreeSet<>()).add(word);
            }
        }
        for (Set<String> group : groups.values())
            if (group.size() >= minGroupSize)
                System.out.println(group.size() + ": " + group);
    }
    private static String alphabetize(String s) {
        char[] a = s.toCharArray();
        Arrays.sort(a);
        return new String(a);
    }
}
  • ๋งต์— ๊ฐ ๋‹จ์–ด๋ฅผ ์‚ฝ์ž…ํ•  ๋•Œ ์ž๋ฐ” 8์—์„œ ์ถ”๊ฐ€๋œ computeIfAbsent ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.
    ์ด์ฒ˜๋Ÿผ computeIfAbsent๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ ํ‚ค์— ๋‹ค์ˆ˜์˜ ๊ฐ’์„ ๋งคํ•‘ํ•˜๋Š” ๋งต์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
// case2 ์ŠคํŠธ๋ฆผ์„ ๊ณผ๋„ํ•˜ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ

public class Anagrams {
    public static void main(String[] args) throws IOException {
        Path dictionary = Paths.get(args[0]);
        int minGroupSize = Integer.parseInt(args[1]);
        try (Stream<String> words = Files.lines(dictionary)) {
            words.collect(
                groupingBy(word -> word.chars().sorted()
                                       .collect(StringBuilder::new,
                                                (sb, c) -> sb.append((char) c),
                                                StringBuilder::append).toString()))
                 .values().stream()
                 .filter(group -> group.size() >= minGroupSize)
                 .map(group -> group.size() + ": " + group)
                 .forEach(System.out::println);
        }
    }
}
  • ์ด ์ฝ”๋“œ๋Š” ํ™•์‹คํžˆ ์งง์ง€๋งŒ ์ฝ๊ธฐ๋Š” ์–ด๋ ต๋‹ค. ํŠนํžˆ ์ŠคํŠธ๋ฆผ์— ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋ผ๋ฉด ๋”์šฑ ๊ทธ๋Ÿด ๊ฒƒ์ด๋‹ค. ์ด์ฒ˜๋Ÿผ ์ŠคํŠธ๋ฆผ์„ ๊ณผ์šฉํ•˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์ด ์ฝ๊ฑฐ๋‚˜ ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง„๋‹ค.
// case3 ์ ๋‹นํ•œ ์ ˆ์ถฉ์•ˆ ์ฝ”๋“œ

public class Anagrams {
    public static void main(String[] args) throws IOException {
        Path dictionary = Paths.get(args[0]);
        int minGroupSize = Integer.parseInt(args[1]);
        try (Stream<String> words = Files.lines(dictionary)) {
            words.collect(groupingBy(word -> alphabetize(word)))
                 .values().stream()
                 .filter(group -> group.size() >= minGroupSize)
                 .forEach(g -> System.out.println(g.size() + ": " + g));
        }
    }
    // alphabetize ๋ฉ”์„œ๋“œ๋Š” ์ฝ”๋“œ 45-1๊ณผ ๊ฐ™๋‹ค.
}
  • ๋žŒ๋‹ค์—์„œ๋Š” ํƒ€์ž… ์ด๋ฆ„์„ ์ž์ฃผ ์ƒ๋žตํ•˜๋ฏ€๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ์ž˜ ์ง€์–ด์•ผ ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๊ฐ€๋…์„ฑ์ด ์œ ์ง€๋œ๋‹ค.
  • ํ•œํŽธ, ๋‹จ์–ด์˜ ์ฒ ์ž๋ฅผ ์•ŒํŒŒ๋ฒณ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ์ผ์€ ๋ณ„๋„ ๋ฉ”์„œ๋“œ์ธ alphabetize์—์„œ ์ˆ˜ํ–‰ํ–ˆ๋‹ค. ์—ฐ์‚ฐ์— ์ ์ ˆํ•œ ์ด๋ฆ„์„ ์ง€์–ด์ฃผ๊ณ  ์„ธ๋ถ€ ๊ตฌํ˜„์„ ์ฃผ ํ”„๋กœ๊ทธ๋žจ ๋กœ์ง ๋ฐ–์œผ๋กœ ๋นผ๋‚ด ์ „์ฒด์ ์ธ ๊ฐ€๋…์„ฑ์„ ๋†’์ธ ๊ฒƒ์ด๋‹ค. ๋„์šฐ๋ฏธ ๋ฉ”์„œ๋“œ๋ฅผ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜๋Š” ์ผ์˜ ์ค‘์š”์„ฑ์€ ์ผ๋ฐ˜ ๋ฐ˜๋ณต ์ฝ”๋“œ์—์„œ๋ณด๋‹ค๋Š” ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ํ›จ์”ฌ ํฌ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์ŠคํŠธ๋ฆผ์€ ์–ธ์ œ์จ์•ผ ํ• ๊นŒ?

์ฃผ์˜์‚ฌํ•ญ

  • ์ฝ”๋“œ ๋ธ”๋ก์—์„œ๋Š” ๋ฒ”์œ„ ์•ˆ์˜ ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ์ฝ๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋žŒ๋‹ค์—์„œ๋Š” final์ด๊ฑฐ๋‚˜ ์‚ฌ์‹ค์ƒ final์ธ ๋ณ€์ˆ˜[JLS 4.12.4]๋งŒ ์ฝ์„ ์ˆ˜ ์žˆ๊ณ , ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฑด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ์ฝ”๋“œ ๋ธ”๋ก์—์„œ๋Š” return ๋ฌธ์„ ์‚ฌ์šฉํ•ด ๋ฉ”์„œ๋“œ์—์„œ ๋น ์ ธ๋‚˜๊ฐ€๊ฑฐ๋‚˜, break๋‚˜ continue ๋ฌธ์œผ๋กœ ๋ธ”๋ก ๋ฐ”๊นฅ์˜ ๋ฐ˜๋ณต๋ฌธ์„ ์ข…๋ฃŒํ•˜๊ฑฐ๋‚˜ ๋ฐ˜๋ณต์„ ํ•œ ๋ฒˆ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ ๋ฉ”์„œ๋“œ ์„ ์–ธ์— ๋ช…์‹œ๋œ ๊ฒ€์‚ฌ ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋žŒ๋‹ค๋กœ๋Š” ์ด ์ค‘ ์–ด๋–ค ๊ฒƒ๋„ ํ•  ์ˆ˜ ์—†๋‹ค.

์•ˆ์„ฑ๋งž์ถค์ธ ์ƒํ™ฉ

  • ์›์†Œ๋“ค์˜ ์‹œํ€€์Šค๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ๋ณ€ํ™˜ํ•œ๋‹ค.
  • ์›์†Œ๋“ค์˜ ์‹œํ€€์Šค๋ฅผ ํ•„ํ„ฐ๋งํ•œ๋‹ค.
  • ์›์†Œ๋“ค์˜ ์‹œํ€€์Šค๋ฅผ ํ•˜๋‚˜์˜ ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•ด ๊ฒฐํ•ฉํ•œ๋‹ค(๋”ํ•˜๊ธฐ, ์—ฐ๊ฒฐํ•˜๊ธฐ, ์ตœ์†Ÿ ๊ฐ’ ๊ตฌํ•˜๊ธฐ ๋“ฑ).
  • ์›์†Œ๋“ค์˜ ์‹œํ€€์Šค๋ฅผ ์ปฌ๋ ‰์…˜์— ๋ชจ์€๋‹ค(์•„๋งˆ๋„ ๊ณตํ†ต๋œ ์†์„ฑ์„ ๊ธฐ์ค€์œผ๋กœ ๋ฌถ์–ด๊ฐ€๋ฉฐ).
  • ์›์†Œ๋“ค์˜ ์‹œํ€€์Šค์—์„œ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์›์†Œ๋ฅผ ์ฐพ๋Š”๋‹ค.

์ ์šฉ์ด ์–ด๋ ค์šด ์ƒํ™ฉ

  • ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„(stage)๋ฅผ ํ†ต๊ณผํ•  ๋•Œ ์ด ๋ฐ์ดํ„ฐ์˜ ๊ฐ ๋‹จ๊ณ„์—์„œ์˜ ๊ฐ’๋“ค์— ๋™์‹œ์— ์ ‘๊ทผํ•˜๊ธฐ๋Š” ์–ด๋ ค์šด ๊ฒฝ์šฐ๋‹ค. ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์€ ์ผ๋‹จ ํ•œ ๊ฐ’์„ ๋‹ค๋ฅธ ๊ฐ’์— ๋งคํ•‘ํ•˜๊ณ  ๋‚˜๋ฉด ์›๋ž˜์˜ ๊ฐ’์€ ์žƒ๋Š” ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

ํ•ต์‹ฌ์ •๋ฆฌ

์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•ด์•ผ ๋ฉ‹์ง€๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์ด ์žˆ๊ณ , ๋ฐ˜๋ณต ๋ฐฉ์‹์ด ๋” ์•Œ๋งž์€ ์ผ๋„ ์žˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์ˆ˜๋งŽ์€ ์ž‘์—…์ด ์ด ๋‘˜์„ ์กฐํ•ฉํ–ˆ์„ ๋•Œ ๊ฐ€์žฅ ๋ฉ‹์ง€๊ฒŒ ํ•ด๊ฒฐ๋œ๋‹ค.
์–ด๋Š ์ชฝ์„ ์„ ํƒํ•˜๋Š” ํ™•๊ณ ๋ถ€๋™ํ•œ ๊ทœ์น™์€ ์—†์ง€๋งŒ ์ฐธ๊ณ ํ•  ๋งŒํ•œ ์ง€์นจ ์ •๋„๋Š” ์žˆ๋‹ค.
์–ด๋Š ์ชฝ์ด ๋‚˜์€์ง€๊ฐ€ ํ™•์—ฐํžˆ ๋“œ๋Ÿฌ๋‚˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ฒ ์ง€๋งŒ, ์•„๋‹ˆ๋”๋ผ๋„ ๋ฐฉ๋ฒ•์€ ์žˆ๋‹ค.
์ŠคํŠธ๋ฆผ๊ณผ ๋ฐ˜๋ณต ์ค‘ ์–ด๋Š ์ชฝ์ด ๋‚˜์€์ง€ ํ™•์‹ ํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋ฉด ๋‘˜ ๋‹ค ํ•ด๋ณด๊ณ  ๋” ๋‚˜์€ ์ชฝ์„ ํƒํ•˜๋ผ.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ