๐ 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 ๊ณ์ด(List, Set, Queue ๋ฑ)์๋ง ์ง์ ์ฌ์ฉํ ์ ์์.
- 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() (๋ ๋ค ๋ณ๋ ฌ ์ต์ ํ ๊ฐ๋ฅ) |
* ์ฑ๋ฅ ๋น๊ต ์ฐธ๊ณ ์๋ฃ
'Backend > JAVA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ํ์ผ ์ ๋ก๋, ๋ค์ด๋ก๋ (MultipartFile) (0) | 2025.11.08 |
|---|---|
| JUnit ๋จ์ ํ ์คํธ ์ ์ฉ, JaCoCo ์ปค๋ฒ๋ฆฌ์ง ์ธก์ (0) | 2025.08.27 |
| static ํค์๋ (0) | 2025.02.26 |
| ์๋ฐ(Spring) ๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ (0) | 2025.02.23 |
| [Java] ๋น๋๊ธฐ ์์ ์ฒ๋ฆฌ (ExecutorService VS CompletableFuture) (0) | 2025.02.23 |