Backend/JAVA

Stream API๋ž€? (for-loop์™€ ๋น„๊ต)

dddzr 2025. 8. 9. 16:12

๐Ÿ“Œ 1. Stream API๋ž€?

Stream API๋Š” Java 8์—์„œ ๋„์ž…๋œ ๊ธฐ๋Šฅ์œผ๋กœ, ์ปฌ๋ ‰์…˜(List, Set ๋“ฑ)์ด๋‚˜ ๋ฐฐ์—ด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” API.
๊ฐ„๊ฒฐํ•˜๊ณ  ์ง๊ด€์ ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฐ˜๋ณต๋ฌธ๋ณด๋‹ค ๊ฐ€๋…์„ฑ์ด ์ข‹๊ณ  ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋„ ์ง€์›ํ•œ๋‹ค.

 

๐Ÿ“Œ 2. Stream์˜ ํŠน์ง•

  • ์›๋ณธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Œ (๋ถˆ๋ณ€์„ฑ ์œ ์ง€)
  • ์ค‘๊ฐ„ ์—ฐ์‚ฐ(Intermediate)๊ณผ ์ตœ์ข… ์—ฐ์‚ฐ(Terminal)์œผ๋กœ ๋‚˜๋‰จ
  • ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ(parallelStream()) ๊ฐ€๋Šฅ

 

๐Ÿ“Œ 3. Stream API ๊ธฐ๋ณธ ๋™์ž‘ ํ๋ฆ„

Stream API๋Š” "๋ฐ์ดํ„ฐ(Stream) ์ƒ์„ฑ → ์ค‘๊ฐ„ ์—ฐ์‚ฐ (ํ•„ํ„ฐ๋ง, ๋ณ€ํ™˜ ๋“ฑ) → ์ตœ์ข… ์—ฐ์‚ฐ (๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜, ์ถœ๋ ฅ ๋“ฑ) " ์˜ ๊ตฌ์กฐ๋กœ ์ž‘๋™ํ•œ๋‹ค.

 

๐Ÿ“– ์˜ˆ์ œ: ์ˆซ์ž ๋ฆฌ์ŠคํŠธ์—์„œ ์ง์ˆ˜๋งŒ ๊ณจ๋ผ ์ œ๊ณฑ ํ›„ ํ•ฉ์‚ฐ

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
int sum = numbers.stream()          // 1๏ธโƒฃ Stream ์ƒ์„ฑ
                 .filter(n -> n % 2 == 0) // 2๏ธโƒฃ ์ค‘๊ฐ„ ์—ฐ์‚ฐ (์ง์ˆ˜ ํ•„ํ„ฐ)
                 .map(n -> n * n)         // 3๏ธโƒฃ ์ค‘๊ฐ„ ์—ฐ์‚ฐ (์ œ๊ณฑ)
                 .reduce(0, Integer::sum); // 4๏ธโƒฃ ์ตœ์ข… ์—ฐ์‚ฐ (ํ•ฉ๊ณ„ ๊ณ„์‚ฐ)

System.out.println(sum); // 56 (2*2 + 4*4 + 6*6)

 

๐Ÿ“Œ 4. Stream API ์ฃผ์š” ๋ฉ”์„œ๋“œ ์ •๋ฆฌ

์ข…๋ฅ˜ ๋ฉ”์„œ๋“œ ์„ค๋ช…
์ƒ์„ฑ stream() ์ปฌ๋ ‰์…˜์—์„œ Stream ์ƒ์„ฑ
  Arrays.stream() ๋ฐฐ์—ด์—์„œ Stream ์ƒ์„ฑ
  Stream.of() ๊ฐœ๋ณ„ ์š”์†Œ๋กœ Stream ์ƒ์„ฑ
์ค‘๊ฐ„ ์—ฐ์‚ฐ (Intermediate) filter(Predicate) ์กฐ๊ฑด์— ๋งž๋Š” ์š”์†Œ๋งŒ ํ•„ํ„ฐ๋ง
  map(Function) ์š”์†Œ ๋ณ€ํ™˜ (ex. ์†Œ๋ฌธ์ž → ๋Œ€๋ฌธ์ž)
  sorted() ์ •๋ ฌ
  distinct() ์ค‘๋ณต ์ œ๊ฑฐ
  limit(n) n๊ฐœ์˜ ์š”์†Œ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ
  skip(n) ์ฒ˜์Œ n๊ฐœ์˜ ์š”์†Œ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
์ตœ์ข… ์—ฐ์‚ฐ (Terminal) collect(Collectors.toList()) ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜
  forEach() ๊ฐ ์š”์†Œ๋ฅผ ๋ฐ˜๋ณตํ•˜๋ฉฐ ์‹คํ–‰
  count() ์š”์†Œ ๊ฐœ์ˆ˜ ๋ฐ˜ํ™˜
  reduce() ๋ˆ„์  ์—ฐ์‚ฐ ์ˆ˜ํ–‰
  anyMatch(), allMatch(), noneMatch() ์กฐ๊ฑด ๊ฒ€์‚ฌ

 

๐Ÿ“Œ 5. Stream ํ•จ์ˆ˜ ์˜ˆ์ œ

โœ…5-1. filter() - ํŠน์ • ์กฐ๊ฑด์˜ ์š”์†Œ๋งŒ ์„ ํƒ

List<String> fruits = Arrays.asList("apple", "banana", "cherry", "avocado");
List<String> result = fruits.stream()
                            .filter(fruit -> fruit.startsWith("a")) // 'a'๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ๋งŒ
                            .collect(Collectors.toList());

System.out.println(result); // [apple, avocado]

 

โœ…5-2. map() - ์š”์†Œ ๋ณ€ํ™˜

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> squares = numbers.stream()
                               .map(n -> n * n) // ์ œ๊ณฑ
                               .collect(Collectors.toList());

System.out.println(squares); // [1, 4, 9, 16]

List<String> names = Arrays.asList("apple", "banana", "cherry");
List<String> upperCaseNames = names.stream()
                                   .map(String::toUpperCase)
                                   .collect(Collectors.toList());

System.out.println(upperCaseNames); // [APPLE, BANANA, CHERRY]

 

โœ…5-3. sorted() - ์ •๋ ฌ

List<String> names = Arrays.asList("John", "Alice", "Bob");
List<String> sortedNames = names.stream()
                                .sorted()
                                .collect(Collectors.toList());

System.out.println(sortedNames); // [Alice, Bob, John]

 

 

โœ…5-4. reduce() - ๋ˆ„์  ์—ฐ์‚ฐ

๋‹จ์ผ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

// 1๏ธโƒฃ ์ˆซ์ž
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
                 .reduce(0, Integer::sum); // (์ดˆ๊ธฐ๊ฐ’, ๋ˆ„์ ์—ฐ์‚ฐ)

System.out.println(sum); // 15 (1+2+3+4+5)

// 2๏ธโƒฃ ๋ฌธ์ž์—ด
String combined = Stream.of("A", "B", "C")
                .reduce("", (a, b) -> a + b);
System.out.println(combined); // ABC

// 3๏ธโƒฃ ๊ฐ์ฒด
Person p1 = new Person("Alice", 20);
Person p2 = new Person("Bob", 25);

Person merged = Stream.of(p1, p2)
                .reduce(new Person(), (a, b) -> a.merge(b));
System.out.println(merged); // Person{name='AliceBob', age=45}

 

 

โœ…5-5. collect() - ๊ฐ’์„ ์ˆ˜์ง‘/๋ณ€ํ™˜

์ปฌ๋ ‰์…˜์ด๋‚˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

// 1๏ธโƒฃ List ๋ฐ˜ํ™˜
List<String> list = Stream.of("A", "B", "C")
        .collect(Collectors.toList());
System.out.println(list); // [A, B, C]

// 2๏ธโƒฃ Set ๋ฐ˜ํ™˜
Set<String> set = Stream.of("A", "B", "A")
        .collect(Collectors.toSet());
System.out.println(set); // [A, B]

// 3๏ธโƒฃ Map ๋ฐ˜ํ™˜
Map<String, Integer> map = Stream.of("A", "B", "C")
        .collect(Collectors.toMap(s -> s, String::length));
System.out.println(map); // {A=1, B=1, C=1}

// 4๏ธโƒฃ String ๋ฐ˜ํ™˜
String joined = Stream.of("A", "B", "C")
        .collect(Collectors.joining("-"));
System.out.println(joined); // A-B-C

// 5๏ธโƒฃ ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๋ฐ˜ํ™˜
StringBuilder sb = Stream.of("A", "B", "C")
        .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append);
System.out.println(sb); // ABC

 

๐Ÿ“Œ 6. ์ž๋ฃŒํ˜• ๋ณ„ Stream

Stream API๋Š” Collection ๊ณ„์—ด(ListSetQueue ๋“ฑ)์—๋งŒ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

  • Collection: ์š”์†Œ ๊ด€๋ฆฌ์šฉ → Stream ํ™œ์šฉ ๊ฐ€๋Šฅ
  • Map: ํ‚ค ๊ธฐ๋ฐ˜ ์กฐํšŒ์šฉ → keySet(), values(), entrySet()๋กœ Stream ์ฒ˜๋ฆฌ
  • ๋ฐฐ์—ด[]: ๊ณ ์ • ํฌ๊ธฐ, ๋ฉ”๋ชจ๋ฆฌ ์—ฐ์† → ์†๋„ ๋น ๋ฆ„,  Arrays.stream() ์‚ฌ์šฉ์œผ๋กœ Stream ๊ฐ€๋Šฅ

 

๐Ÿ“– Map์— Stream ์‚ฌ์šฉ ์˜ˆ์‹œ (์ •๋ ฌ)

Map → Collection(Set) → Stream ์ˆœ์„œ๋กœ ๋ณ€ํ™˜

//๐Ÿ“Œ 1. Java 8 Stream ํ™œ์šฉ (๊ฐ€์žฅ ๊ฐ„๋‹จ)
import java.util.*;
import java.util.stream.Collectors;

public class SortMapByValue {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("banana", 2);
        map.put("apple", 5);
        map.put("cherry", 1);

        // Integer ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ ํ›„ key๋งŒ ๋ฝ‘๊ธฐ
        List<String> sortedKeys = map.entrySet().stream()
                .sorted(Map.Entry.comparingByValue()) // value ๊ธฐ์ค€ ์ •๋ ฌ
                .map(Map.Entry::getKey)               // key๋งŒ ์ถ”์ถœ
                .collect(Collectors.toList());

        System.out.println(sortedKeys); // [cherry, banana, apple]
    }
}

//๐Ÿ“Œ 2. ์—ญ์ˆœ(๋‚ด๋ฆผ์ฐจ์ˆœ) ์ •๋ ฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด
List<String> sortedKeysDesc = map.entrySet().stream()
        .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
        .map(Map.Entry::getKey)
        .collect(Collectors.toList());

System.out.println(sortedKeysDesc); // [apple, banana, cherry]

//๐Ÿ“Œ 3. ๋žŒ๋‹ค์‹ ์‚ฌ์šฉ
List<String> sortedKeys = map.entrySet().stream()
        .sorted((e1, e2) -> e1.getValue().compareTo(e2.getValue()))
        .map(Map.Entry::getKey)
        .collect(Collectors.toList());


//๐Ÿ“Œ 4. Java 7 ์Šคํƒ€์ผ (์ŠคํŠธ๋ฆผ ์—†๋Š” ๋ฐฉ์‹)
List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
list.sort((e1, e2) -> e1.getValue().compareTo(e2.getValue()));

List<String> sortedKeys = new ArrayList<>();
for (Map.Entry<String, Integer> entry : list) {
    sortedKeys.add(entry.getKey());
}

 

๐Ÿ“Œ 7. parallelStream() - ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ

Stream์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆœ์ฐจ ์ฒ˜๋ฆฌ์ง€๋งŒ, ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ(parallelStream())๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ ํ–ฅ์ƒ ๊ฐ€๋Šฅ

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
                 .map(n -> n * n)
                 .reduce(0, Integer::sum);

System.out.println(sum); // 55 (1*1 + 2*2 + ... + 5*5)

 

โœ” CPU ์ฝ”์–ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋ณ‘๋ ฌ ์‹คํ–‰
โœ” ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์— ์œ ๋ฆฌํ•˜์ง€๋งŒ, ์ž‘์€ ๋ฐ์ดํ„ฐ์—์„œ๋Š” stream()์ด ๋” ์ ํ•ฉ

 

๐Ÿ“Œ 8. stream ๋ฐฉ์‹ vs for ๋ฃจํ”„ ๋ฐฉ์‹ ๋น„๊ต

๐Ÿ”ฅ Stream API vs for ๋ฃจํ”„ ์š”์•ฝ

๋ฐฉ์‹ ์žฅ์  ๋‹จ์ 
Stream API (map()) โœ”๏ธ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•˜๊ณ  ๊ฐ€๋…์„ฑ์ด ์ข‹์Œ 
โœ”๏ธ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ (parallelStream())
โœ”๏ธ๋ถˆ๋ณ€์„ฑ ์œ ์ง€

โŒ ๋””๋ฒ„๊น…์ด ์–ด๋ ค์›€
โŒ ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ
for ๋ฃจํ”„ โœ”๏ธ๋ช…ํ™•ํ•œ ์ ˆ์ฐจ์  ์ฝ”๋“œ
โœ”๏ธ๋””๋ฒ„๊น…์ด ์‰ฌ์›€
โœ”๏ธ๋ฐ˜๋ณต ์ค‘ ๊ฐœ๋ณ„์ ์ธ ๋กœ์ง ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
โŒ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ง€๊ณ  ๊ฐ€๋…์„ฑ์ด ๋‚ฎ์Œ
โŒ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์ง€์› ์•ˆ ๋จ

 

โœ…8-1. Stream API ์‚ฌ์šฉ

List<ProductDetailDTO.ProductStockDto> stockDTOs = product.getStocks().stream()
    .map(stock -> new ProductStockDto(
        stock.getSize().getSizeName(),
        stock.getColor().getColorName(),
        stock.getStockQuantity()))
    .collect(Collectors.toList());

 

๐Ÿ”น ํŠน์ง•

  • ํ•œ ์ค„๋กœ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ (map() ํ™œ์šฉ)
  • ๋ถˆ๋ณ€์„ฑ ์œ ์ง€ → ์›๋ณธ ๋ฆฌ์ŠคํŠธ(product.getStocks())๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Œ
  • ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ (.parallelStream()์œผ๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ฐ€๋Šฅ)
  • ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ๋กœ ๊ฐ€๋…์„ฑ์ด ์ข‹์Œ

 

๐Ÿ”น์ž‘๋™ ๋ฐฉ์‹
1๏ธโƒฃ product.getStocks().stream() → Stock ๋ฆฌ์ŠคํŠธ๋ฅผ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜
2๏ธโƒฃ .map(stock -> new ProductStockDto(...)) → Stock ๊ฐ์ฒด๋ฅผ ProductStockDto๋กœ ๋ณ€ํ™˜
3๏ธโƒฃ .collect(Collectors.toList()) → ๋ณ€ํ™˜๋œ DTO ๋ฆฌ์ŠคํŠธ๋กœ ์ˆ˜์ง‘

 

โœ…8-2. for ๋ฃจํ”„ ์‚ฌ์šฉ ๋ฐฉ์‹

List<ProductDetailDTO.ProductStockDto> stockDTOs = new ArrayList<>();
for (int i = 0; i < product.getStocks().size(); i++) {
    ProductStocks stock = product.getStocks().get(i);
    ProductDetailDTO.ProductStockDto dto = new ProductDetailDTO.ProductStockDto();
    dto.setSizeName(stock.getSize().getSizeName());
    dto.setColorName(stock.getColor().getColorName());
    dto.setStockQuantity(stock.getStockQuantity());
    stockDTOs.add(dto);
}

 

๐Ÿ”น ํŠน์ง•

  • ๋ช…ํ™•ํ•œ ์ ˆ์ฐจ์  ์ ‘๊ทผ → ๋””๋ฒ„๊น…์ด ์‰ฌ์›€
  • .stream() ์—†์ด ๊ธฐ๋ณธ์ ์ธ for ๋ฐ˜๋ณต๋ฌธ ์‚ฌ์šฉ
  • ๋ณ€์ˆ˜ ์กฐ์ž‘ ๊ฐ€๋Šฅ (๋ฐ˜๋ณต ์ค‘ ํŠน์ • ์กฐ๊ฑด์—์„œ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ)
  • ์„ฑ๋Šฅ ์ฐจ์ด๋Š” ๊ฑฐ์˜ ์—†์Œ (Stream์€ ๋‚ด๋ถ€์ ์œผ๋กœ ์ตœ์ ํ™”๋จ)

 

๐Ÿ”น ์ž‘๋™ ๋ฐฉ์‹
1๏ธโƒฃ new ArrayList<>() ๋กœ ๋นˆ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
2๏ธโƒฃ for ๋ฃจํ”„๋ฅผ ๋Œ๋ฉฐ Stock ๊ฐ์ฒด ํ•˜๋‚˜์”ฉ ๊ฐ€์ ธ์˜ด
3๏ธโƒฃ new ProductStockDto()๋กœ DTO ์ƒ์„ฑ
4๏ธโƒฃ setXXX()์œผ๋กœ ํ•„๋“œ ๊ฐ’ ์„ค์ •
5๏ธโƒฃ stockDTOs.add(dto)๋กœ ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€

 

๐Ÿš€ Stream API ์–ธ์ œ ์จ์•ผ ํ• ๊นŒ?

โœ… ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง, ๋ณ€ํ™˜, ์ˆ˜์ง‘์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ๋•Œ
โœ… ๊ฐ€๋…์„ฑ์„ ๋†’์ด๊ณ  ์ฝ”๋“œ ๊ธธ์ด๋ฅผ ์ค„์ด๊ณ  ์‹ถ์„ ๋•Œ
โœ… ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ(parallelStream())ํ•˜๊ณ  ์‹ถ์„ ๋•Œ

โŒ ๋‹จ์ˆœ ๋ฐ˜๋ณต๋ฌธ๋ณด๋‹ค ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•  ๊ฒฝ์šฐ (for ๋ฌธ์ด ๋” ๋น ๋ฅผ ์ˆ˜๋„ ์žˆ์Œ)



๐Ÿ“Œ 9. for, stream, stream+๋ณ‘๋ ฌ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ

๐Ÿ’ก ์„ฑ๋Šฅ ๋น„๊ต ๊ธฐ์ค€

1๏ธโƒฃ ์ปฌ๋ ‰์…˜ ํฌ๊ธฐ - ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก stream()์ด ์œ ๋ฆฌํ•ด์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Œ
2๏ธโƒฃ ์—ฐ์‚ฐ์˜ ๋ณต์žก๋„ - ์—ฐ์‚ฐ์ด ๊ฐ€๋ฒผ์šฐ๋ฉด for ๋ฌธ์ด ๋น ๋ฅด๊ณ , ์—ฐ์‚ฐ์ด ๋ฌด๊ฑฐ์šฐ๋ฉด stream()์ด ์œ ๋ฆฌํ•จ
3๏ธโƒฃ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ๊ฐ€? - CPU-intensive(์—ฐ์‚ฐ๋Ÿ‰์ด ๋งŽ์€) ์ž‘์—…์ด๋ฉด parallelStream()์ด ์œ ๋ฆฌ

 

๐Ÿ”ฅ for vs stream() vs parallelStream() ์ถ”์ฒœ

์ƒํ™ฉ for ๋ฌธ stream() parallelStream()
๋ฐ์ดํ„ฐ ํฌ๊ธฐ ์ž‘์€ ๊ฒฝ์šฐ ์œ ๋ฆฌ (100๊ฐœ ์ดํ•˜) ์ค‘๊ฐ„ ํฌ๊ธฐ (1,000๊ฐœ ์ด์ƒ) ๋งค์šฐ ํฐ ๊ฒฝ์šฐ (100,000๊ฐœ ์ด์ƒ)
์—ฐ์‚ฐ ๋ณต์žก๋„ ๋‹จ์ˆœ ์—ฐ์‚ฐ ๋ณดํ†ต ์—ฐ์‚ฐ (ํ•„ํ„ฐ๋ง, ๋ณ€ํ™˜ ๋“ฑ) ์—ฐ์‚ฐ์ด ๋งค์šฐ ๋ฌด๊ฑฐ์šด ๊ฒฝ์šฐ
๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ํ•„์š” โŒ ์ง€์› ์•ˆ๋จ โŒ ๋‹จ์ผ ์Šค๋ ˆ๋“œ โœ… ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ
๊ฐ€๋…์„ฑ ๋‹ค์†Œ ๊ธธ์–ด์ง โœ… ๊ฐ„๊ฒฐํ•˜๊ณ  ์ง๊ด€์  ๐Ÿšจ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Œ
์ถ”์ฒœ ์˜ˆ์ œ ๋‹จ์ˆœ ํ•ฉ์‚ฐ, ์ธ๋ฑ์Šค ๊ธฐ๋ฐ˜ ๋ฐ˜๋ณต ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง, ๋ณ€ํ™˜ ๋จธ์‹ ๋Ÿฌ๋‹, ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

 

โœ…9-1. for ๋ฌธ์ด ๋” ๋น ๋ฅธ ๊ฒฝ์šฐ (์ž‘์€ ๋ฐ์ดํ„ฐ + ๊ฐ„๋‹จํ•œ ์—ฐ์‚ฐ)

  • ์ปฌ๋ ‰์…˜ ํฌ๊ธฐ๊ฐ€ ์ž‘์Œ (์˜ˆ: 100๊ฐœ ์ดํ•˜)
  • ์—ฐ์‚ฐ์ด ๊ฐ„๋‹จํ•จ (ex. ๋‹จ์ˆœํ•œ ๋ง์…ˆ, ๊ณฑ์…ˆ, ๋Œ€์ž… ์—ฐ์‚ฐ)
  • ์ƒํƒœ๋ฅผ ์ง์ ‘ ์กฐ์ž‘ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ (ex. ์ธ๋ฑ์Šค ๊ธฐ๋ฐ˜ ์ ‘๊ทผ์ด ํ•„์š”ํ•  ๋•Œ)

 

๐Ÿ“– ์˜ˆ์ œ: for ๋ฌธ์ด ๋น ๋ฅธ ๊ฒฝ์šฐ

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;
for (int num : numbers) {
    sum += num;
}

System.out.println(sum); // 15

 

โžก๏ธ for ๋ฌธ์ด ๋‹จ์ˆœํ•œ ๋ฃจํ”„์ผ ๋•Œ๋Š” stream๋ณด๋‹ค ์„ฑ๋Šฅ์ด ๋” ์ข‹์Œ

 

โœ…9-2. stream()์ด ๋” ์œ ๋ฆฌํ•œ ๊ฒฝ์šฐ (ํฐ ๋ฐ์ดํ„ฐ + ๋ณต์žกํ•œ ์—ฐ์‚ฐ)

  • ์ปฌ๋ ‰์…˜ ํฌ๊ธฐ๊ฐ€ ํผ (์˜ˆ: 10,000๊ฐœ ์ด์ƒ)
  • ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง, ๋ณ€ํ™˜, ์ •๋ ฌ์ด ๋งŽ์„ ๋•Œ
  • ๊ฐ€๋…์„ฑ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ (์ฝ”๋“œ๊ฐ€ ์งง๊ณ  ๊ฐ„๊ฒฐํ•ด์ง)
  • ๋‹จ์ผ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•ด๋„ ํšจ์œจ์ ์ธ ๊ฒฝ์šฐ

 

๐Ÿ“– ์˜ˆ์ œ: stream()์ด ์œ ๋ฆฌํ•œ ๊ฒฝ์šฐ (๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง + ๋ณ€ํ™˜)

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> filteredNames = names.stream()
                                  .filter(name -> name.startsWith("A"))
                                  .map(String::toUpperCase)
                                  .collect(Collectors.toList());

System.out.println(filteredNames); // [ALICE]

 

โžก๏ธ ๋ณต์žกํ•œ ์—ฐ์‚ฐ์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ stream()์ด ๊ฐ€๋…์„ฑ์ด ์ข‹๊ณ  ํšจ์œจ์ 

 

โœ…9-3. parallelStream()์ด ์œ ๋ฆฌํ•œ ๊ฒฝ์šฐ (๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ + ์—ฐ์‚ฐ์ด ๋ฌด๊ฑฐ์šด ๊ฒฝ์šฐ)

  • CPU-intensive ์ž‘์—…์—์„œ ์œ ๋ฆฌํ•จ!
  • ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๊ฐ€ ๋งค์šฐ ํผ (์˜ˆ: 100,000๊ฐœ ์ด์ƒ)
  • ๊ฐ ์š”์†Œ์˜ ์—ฐ์‚ฐ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ
  • ์ปฌ๋ ‰์…˜์ด ์‰ฝ๊ฒŒ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ(Splittable Collection, ex. ArrayList, HashSet)

 

โš ๏ธ ์ฃผ์˜! parallelStream()์ด ๋ฌด์กฐ๊ฑด ๋น ๋ฅธ ๊ฒƒ์€ ์•„๋‹˜

  •  ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž‘์€ ๋ฐ์ดํ„ฐ์—์„œ๋Š” for ๋ฌธ๋ณด๋‹ค ๋А๋ฆด ์ˆ˜ ์žˆ์Œ

โžก๏ธ ๋ฐ์ดํ„ฐ ์ˆ˜๊ฐ€ ์ ์„ ๋•Œ:  ์Šค๋ ˆ๋“œ๋ฅผ ๋‚˜๋ˆ„๋Š” ๋น„์šฉ > ๊ณ„์‚ฐ ๋น„์šฉ์ด๊ธฐ ๋•Œ๋ฌธ

  •  LinkedList, TreeSet ๊ฐ™์€ ์ž๋ฃŒ๊ตฌ์กฐ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ split์ด ์–ด๋ ค์›Œ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•˜์ง€ ์•Š์Œ
  • ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๋Š” ์—ฐ์‚ฐ (stateful operation)์€ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์—์„œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
  • Cpu ์„ฑ๋Šฅ์—๋„ ์˜ํ–ฅ.

 

๐Ÿ“– ์˜ˆ์ œ: parallelStream()์ด ์œ ๋ฆฌํ•œ ๊ฒฝ์šฐ (๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ ํฌํ•จ)

List<Integer> numbers = IntStream.range(1, 1_000_000).boxed().collect(Collectors.toList());

long startTime = System.currentTimeMillis();
int sum1 = numbers.stream()
                  .map(n -> n * n)  // ์ œ๊ณฑ ์—ฐ์‚ฐ
                  .reduce(0, Integer::sum);
long endTime = System.currentTimeMillis();

System.out.println("Stream ์‹คํ–‰ ์‹œ๊ฐ„: " + (endTime - startTime) + "ms");

startTime = System.currentTimeMillis();
int sum2 = numbers.parallelStream()
                  .map(n -> n * n)
                  .reduce(0, Integer::sum);
endTime = System.currentTimeMillis();

System.out.println("Parallel Stream ์‹คํ–‰ ์‹œ๊ฐ„: " + (endTime - startTime) + "ms");

 

โžก๏ธ ์—ฐ์‚ฐ์ด ๋ฌด๊ฑฐ์šธ์ˆ˜๋ก parallelStream()์ด ์„ฑ๋Šฅ์ด ๋” ์ข‹์Œ!

 

โœ…9-4. ์„ฑ๋Šฅ ์˜ˆ์ธก: ์ˆœํšŒ๋น„์šฉ๊ณผ ๊ณ„์‚ฐ๋น„์šฉ

์ˆœํšŒ๋น„์šฉ๊ณผ ๊ณ„์‚ฐ๋น„์šฉ์„ ์•Œ๋ฉด for, stream, parallelStream ์ค‘ ๋ญ๊ฐ€ ๋” ์œ ๋ฆฌํ•œ์ง€ ์–ด๋А์ •๋„ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด, 

๋‹จ์ˆœ ๊ณ„์‚ฐ์ผ ๋•Œ, ์ˆœํšŒ๋น„์šฉ์ด ์ปค์ง€๋ฉด for > stream ๊ฒฉ์ฐจ๊ฐ€ ์ค„์–ด๋“ ๋‹ค.

๊ณ„์‚ฐ๋น„์šฉ์ด ์ปค์งˆ ์ˆ˜ ๋ก stream์ด ์œ ๋ฆฌํ•ด์ง„๋‹ค.

 

โœ…9-4-1. ์ˆœํšŒ๋น„์šฉ๊ณผ ๊ณ„์‚ฐ๋น„์šฉ์˜ ์˜๋ฏธ

์š”์†Œ ์˜๋ฏธ ์˜ˆ์‹œ
์ˆœํšŒ๋น„์šฉ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์”ฉ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ๋“œ๋Š” ๋น„์šฉ ๋ฆฌ์ŠคํŠธ์˜ ํฌ๊ธฐ, ์ž๋ฃŒ๊ตฌ์กฐ ํŠน์„ฑ
๊ณ„์‚ฐ๋น„์šฉ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ๋“œ๋Š” ๋น„์šฉ .map(), .filter(), ๋ณต์žกํ•œ ๊ณ„์‚ฐ ๋“ฑ

๐Ÿ’ก ์ˆœํšŒ๋น„์šฉ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์š”์†Œ

  • ์ž๋ฃŒ๊ตฌ์กฐ์˜ ๊ตฌ์กฐ
  • ํฌ๊ธฐ (n ๊ฐœ ํ•ญ๋ชฉ์ด๋ฉด O(n))
  • ์ˆœํšŒ ๋ฐฉ์‹ (Index ๊ธฐ๋ฐ˜ vs Iterator ๊ธฐ๋ฐ˜ ๋“ฑ)

 

โœ… 9-4-2. ๋น„์šฉ ๊ณ„์‚ฐ

๐Ÿ’ก ์ด ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ (T) = ์ˆœํšŒ๋น„์šฉ(Ci) + ๊ณ„์‚ฐ๋น„์šฉ(Cf) + ์˜ค๋ฒ„ํ—ค๋“œ(O)

 

1๏ธโƒฃFor: ์ˆœํšŒ๋„ ์ง์ ‘, ๊ณ„์‚ฐ๋„ ์ง์ ‘ (์˜ค๋ฒ„ํ—ค๋“œ ์—†์Œ)

 T = Ci + Cf

 

2๏ธโƒฃstream: ๋‚ด๋ถ€ ์ˆœํšŒ + ํ•จ์ˆ˜ํ˜• ์ฒ˜๋ฆฌ + ์•ฝ๊ฐ„์˜ ์˜ค๋ฒ„ํ—ค๋“œ(ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ ๋“ฑ)

T = Ci * α + Cf * β + O1 // (α, β๋Š” stream ๋‚ด๋ถ€ ์ฒ˜๋ฆฌ์˜ ์˜ค๋ฒ„ํ—ค๋“œ ๊ณ„์ˆ˜)

 

3๏ธโƒฃparallelStream: ๋ณ‘๋ ฌํ™”๋กœ ์ˆœํšŒ์™€ ๊ณ„์‚ฐ ๋ถ„์‚ฐ + ๋ณ‘๋ ฌ ์˜ค๋ฒ„ํ—ค๋“œ (์Šค๋ ˆ๋“œ, ๋ถ„ํ•  ๋“ฑ)

T = (Ci + Cf) / N + O2 // (N = ์ฝ”์–ด ์ˆ˜)

 

โœ… 9-4-3. ์‹ค์ œ๋กœ ์‹œ๊ฐ„ ์ธก์ •ํ•˜๋Š” ์˜ˆ์‹œ ์ฝ”๋“œ

long start = System.nanoTime();
for (int i = 0; i < list.size(); i++) {
    slowOperation(list.get(i)); // ๊ณ„์‚ฐ๋น„์šฉ
}

long end = System.nanoTime();
System.out.println("for: " + (end - start) / 1_000_000 + "ms");

start = System.nanoTime();
list.stream().forEach(item -> slowOperation(item));
end = System.nanoTime();
System.out.println("stream: " + (end - start) / 1_000_000 + "ms");

start = System.nanoTime();
list.parallelStream().forEach(item -> slowOperation(item));
end = System.nanoTime();
System.out.println("parallelStream: " + (end - start) / 1_000_000 + "ms");s

 

โœ…๐Ÿš€ 9-4-4. ์„ฑ๋Šฅ ์˜ˆ์ธก ๊ฒฐ๋ก 

์ˆœํšŒ๋น„์šฉ(Ci) ๊ณ„์‚ฐ๋น„์šฉ(Cf) ์œ ๋ฆฌํ•œ ๋ฐฉ์‹
๋‚ฎ์Œ ๋‚ฎ์Œ for (์˜ค๋ฒ„ํ—ค๋“œ ์—†์Œ, ๊ฐ€์žฅ ๊ฐ€๋ณ๋‹ค)
๋‚ฎ์Œ ๋†’์Œ parallelStream() (๊ณ„์‚ฐ ๋ณ‘๋ ฌํ™” ์œ ๋ฆฌ)
๋†’์Œ ๋‚ฎ์Œ ์ž๋ฃŒ๊ตฌ์กฐ ๋”ฐ๋ผ ๋‹ค๋ฆ„ (stream์ด ์ตœ์ ํ™”๋œ ๊ฒฝ์šฐ ์œ ๋ฆฌ)
๋†’์Œ ๋†’์Œ parallelStream() (๋‘˜ ๋‹ค ๋ณ‘๋ ฌ ์ตœ์ ํ™” ๊ฐ€๋Šฅ)


* ์„ฑ๋Šฅ ๋น„๊ต ์ฐธ๊ณ  ์ž๋ฃŒ

https://sigridjin.medium.com/java-stream-api%EB%8A%94-%EC%99%9C-for-loop%EB%B3%B4%EB%8B%A4-%EB%8A%90%EB%A6%B4%EA%B9%8C-50dec4b9974b