| 일 | 월 | 화 | 수 | 목 | 금 | 토 | 
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | 
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | 
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 
| 23 | 24 | 25 | 26 | 27 | 28 | 29 | 
| 30 | 
                            Tags
                            
                        
                          
                          - LG Aimers 4th
 - 딥러닝
 - 해커톤
 - ChatGPT
 - 회귀
 - regression
 - PCA
 - LG
 - deep learning
 - 머신러닝
 - 오블완
 - OpenAI
 - supervised learning
 - GPT-4
 - gpt
 - 분류
 - 티스토리챌린지
 - LLM
 - AI
 - 지도학습
 - LG Aimers
 - Classification
 - Machine Learning
 
                            Archives
                            
                        
                          
                          - Today
 
- Total
 
SYDev
[이것이 자바다] Chapter 17. 스트림 요소 처리 본문
1. 스트림이란?
- Java 8부터 컬렉션 및 배열의 요소를 반복 처리하기 위해 스트림 사용 가능
 - List 컬렉션의 stream() method로 Stream 객체를 얻고, forEach() method로 요소를 어떻게 처리할지를 람다식으로 제공
 - Iterator와 차이점
- 내부 반복자이므로, 처리 속도가 빠르고 병렬 처리에 효율적
 - 람다식으로 다양한 요소 처리를 정의 가능
 - 중간 처리와 최종 처리를 수행하도록 파이프 라인 형성 가능
 
 
Stream<String> stream = list.stream();
stream.forEach( item -> item 처리 );
- 예제
package ch17.sec1.exam1;
import java.util.*;
import java.util.stream.Stream;
public class StreamExample {
	public static void main(String[] args) {
		// Set collection 생성
		Set<String> set = new HashSet<>();
		set.add("홍길동");
		set.add("신용권");
		set.add("감자바");
		
		// stream을 이용한 요소 반복 처리
		Stream<String> stream = set.stream();
		stream.forEach( name -> System.out.println(name));
	}
}
2. 내부 반복자
- 외부 반복자: 요소를 collection 바깥쪽으로 반복해서 가져와 처리 - for 문, Iterator
- collection의 요소를 외부로 가져오는 코드, 처리하는 코드를 모두 개발자 코드가 가지고 있어야 함
 - 요소를 하나씩 순차적으로 처리
 
 - 내부 반복자: 요소 처리 방법을 collection 내부로 주입시켜, 요소를 반복 처리 - stream
- 개발자 코드에서 제공한 데이터 처리 코드(람다식)를 가지고 컬렉션 내부에서 요소를 반복 처리
 - 요소들을 분배시켜 병렬 작업 -> 멀티 코어 CPU를 최대한 활용
 
 

- 예제
package ch17.sec2;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class ParallelStreamExample {
	public static void main(String[] args) {
		// List Collection 생성
		List<String> list = new ArrayList<>();
		list.add("name1");
		list.add("name2");
		list.add("name3");
		list.add("name4");
		list.add("name5");
		
		// 병렬 처리
		Stream<String> parallelStream = list.parallelStream();	// 병렬 스트림 얻기
		parallelStream.forEach( name -> {
			System.out.println(name + ": " + Thread.currentThread().getName());
		} );
	}
}

3. 중간 처리와 최종 처리
- stream은 하나 이상 연결될 수 있고, 이를 Stream Pipelines라 부름
 - 오리지널 스트림과 집계 처리 사이의 중간 스트림들은 최종 처리를 위해 요소를 걸러내거나(필터링), 요소를 변환시키거나(매핑), 정렬하는 작업을 수행
 - 최종 처리는 중간 처리에서 정제된 요소들을 반복하거나, 집계(카운팅, 총합, 평균) 작업을 수행
 

- mapToInt(): 객체를 int 값으로 매핑해서, IntStream으로 변환
- 어떤 객체를 어떤 int 값으로 매핑할 것인지는 람다식으로 제공
 
 - IntStream.average(): 요소들의 평균 값 계산
 
// Student Stream
Stream<Student> studentStream = list.stream();
// score Stream
IntStream scoreStream = studentStream.mapToInt( student -> student.getScore() );
// average
double avg = scoreStream.average().getAsDouble();

- 메소드 체이닝 패턴을 이용하여 간결화 가능
 - 스트림 파이프라인으로 구성할 때, 파이프라인의 맨 끝에는 반드시 최종 처리 부분이 존재해야 함
 
doube avg = list.stream()
    .mapToInt(student -> student.getScore())
    .average()
    .getAsDouble();
- 예제
package ch17.sec3;
public class Student {
	private String name;
	private int score;
	
	public Student (String name, int score) {
		this.name = name;
		this.score = score;
	}
	
	public String getName() { return name; }
	public int getScore() { return score; }
}
package ch17.sec3;
import java.util.Arrays;
import java.util.List;
public class StreamPipeLineExample {
	public static void main(String[] args) {
		List<Student> list = Arrays.asList(
				new Student("name1", 10),
				new Student("name2", 20),
				new Student("name3", 30)
		);
		
		double avg = list.stream()
				.mapToInt( student -> student.getScore())
				.average()
				.getAsDouble();
		
		System.out.println(avg);
	}
}
4. 리소스로부터 스트림 얻기


4.1. 컬렉션으로부터 스트림 얻기
- java.util.Collection interface는 stream과 parallelStream() method를 가짐 -> 자식 인터페이스인 List와 Set 인터페이스를 구현한 모든 Collection에서 객체 스트림을 얻을 수 있음
 
- 예제
package ch17.sec4.exam1;
public class Product {
	private int pno;
	private String name;
	private String company;
	private int price;
	
	public Product(int pno, String name, String company, int price) {
		this.pno = pno;
		this.name = name;
		this.company = company;
		this.price = price;
	}
	
	public int getPno() { return pno; }
	public String getName() { return name; }
	public String getCompany() { return company; }
	public int getPrice() { return price; }
	
	@Override
	public String toString() {
		return new StringBuilder()
				.append("{")
				.append("pno: " + pno + ", ")
				.append("name: " + name + ", ")
				.append("company: " + company + ", ")
				.append("price: " + price + ", ")
				.append("}")
				.toString();
	}
}
package ch17.sec4.exam1;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamExample {
	public static void main(String[] args) {
		// List collection 생성
		List<Product> list = new ArrayList<>();
		for(int i = 1; i <= 5; i++) {
			Product product = new Product(i, "상품" + i, "멋진 회사 ", (int)(10000 * Math.random()));
			list.add(product);
		}
		
		// 객체 스트림 얻기
		Stream<Product> stream = list.stream();
		stream.forEach(p -> System.out.println(p));
	}
}

4.2. 배열로부터 스트림 얻기
- java.util.Arrays 클래스를 이용하면 다양한 종류의 배열로부터 스트림을 얻을 수 있음
 
- 예제
package ch17.sec4.exam2;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamExample {
	public static void main(String[] args) {
		String[] strArray = { "name1", "name2", "name3" };
		Stream<String> strStream = Arrays.stream(strArray);
		strStream.forEach(item -> System.out.print(item + ","));
		System.out.println();
		
		int[] intArray = { 1, 2, 3, 4, 5 };
		IntStream intStream = Arrays.stream(intArray);
		intStream.forEach(item -> System.out.print(item + ","));
		System.out.println();
	}
}
4.3. 숫자 범위로부터 스트림 얻기
- IntStream 혹은 LongStream의 static method인 range()와 rangeClosed() method -> 특정 범위의 정수 스트림을 얻을 수 있음
 - 끝 수를 포함하면 range(), 포함하지 않으면 rangeClosed()
 
package ch17.sec4.exam3;
import java.util.stream.IntStream;
public class StreamExample {
	public static int sum;
	
	public static void main(String[] args) {
		IntStream stream = IntStream.rangeClosed(1, 100);
		stream.forEach(a -> sum += a);
		System.out.println("sum: " + sum);
	}
}
4.4. 파일로부터 스트림 얻기
- java.nio.file.Files의 lines() method-> 텍스트 파일의 행 단위 스트림을 얻을 수 있음
 
- 예제
package ch17.sec4.exam4;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class StreamExample {
	public static void main(String[] args) throws Exception {
		Path path = Paths.get(StreamExample.class.getResource("data.txt").toURI());
		Stream<String> stream = Files.lines(path, Charset.defaultCharset());
		stream.forEach(line -> System.out.println(line));
		stream.close();
	}
}
5. 요소 걸러내기(필터링)

- distinct(): 요소의 중복 제거
- 객체 스트림(Stream)일 경우, equals() method의 리턴값이 true이면 동일한 요소로 판단
 - IntStream, LongStream, DoubleStream일 경우, 값은 값이면 중복 제거
 
 - filter(): 매개값으로 주어진 Predicate가 true를 리턴하는 요소만 필터링
- Predicate: 함수형 인터페이스로, 매개값을 조사한 후 boolean을 리턴하는 test() method를 가짐
 
 



Predicate<T>를 람다식으로 표현하면 아래와 같음
T -> { ... return true }
T -> true;    // return 문만 있을 경우, 중괄호와 return 키워드 생략 가능
package ch17.sec5;
import java.util.List;
import java.util.ArrayList;
public class FilteringExample {
	public static void main(String[] args) {
		// List Collection 생성
		List<String> list = new ArrayList<>();
		list.add("name1");	list.add("name2");
		list.add("name3");	list.add("name2"); list.add("name5");
		list.add("name3");	list.add("name23");
		
		// 중복 요소 제거
		list.stream()
			.distinct()
			.forEach(n -> System.out.println(n));
		System.out.println();
		
		// 3으로 끝나는 요소만 필터링
		list.stream()
			.filter(n -> n.endsWith("3"))
			.forEach(n -> System.out.println(n));
		System.out.println();
		
		// 중복 요소를 먼저 제거하고, 3으로 끝나는 요소만 필터링
		list.stream()
			.distinct()
			.filter(n -> n.endsWith("3"))
			.forEach(n -> System.out.println(n));
	}
}

6. 요소 변환(매핑)
- mapping: 스트림의 요소를 다른 요소로 변환하는 중간 처리 기능
 - mapXxx(), asDoubleStream(), asLongStream(), boxed(), flatMapXxx(), ...
 
6.1. 요소를 다른 요소로 변환
- mapXxx(): 요소를 다른 요소로 변환한 새로운 스트림을 리턴
 


- 매개타입인 Function은 함수형 인터페이스로, 다음과 같은 종류가 존재
 - applyXxx(): 매개값을 리턴값으로 매핑(변환)
 

T -> { ... return R; }
T -> R;    // return 문만 있을 경우, 중괄호와 return 키워드 생략 가능
- 예제
package ch17.sec6.exam1;
public class Student {
	private String name;
	private int score;
	
	public Student(String name, int score) {
		this.name = name;
		this.score = score;
	}
	
	public String getName() { return name; }
	public int getScore() { return score; }
}
package ch17.sec6.exam1;
import java.util.List;
import java.util.ArrayList;
public class MapExample {
	public static void main(String[] args) {
		// List collection 생성
		List<Student> studentList = new ArrayList<>();
		studentList.add(new Student("name1", 85));
		studentList.add(new Student("name1", 92));
		studentList.add(new Student("name1", 87));
		
		// Student를 score 스트림으로 변환
		studentList.stream()
			.mapToInt(s -> s.getScore())
			.forEach(score -> System.out.println(score));
	}
}
기본 타입 간의 변환이거나, 기본 타입 요소를 Wrapper 객체 요소로 변화하려면 다음 간편화 메소드 사용

6.2. 요소를 복수 개의 요소로 변환
- flatMapXxx(): 하나의 요소를 복수 개의 요소들로 변환한 새로운 스트림 리턴
 


- 예제
package ch17.sec6.exam3;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FlatMappingExample {
	public static void main(String[] args) {
		// 문장 스트림을 단어 스트림으로 변환
		List<String> list1 = new ArrayList<>();
		list1.add("this is java");
		list1.add("i am a best developer");
		list1.stream().
			flatMap(data -> Arrays.stream(data.split(" ")))
			.forEach(word -> System.out.println(word));
		
		System.out.println();
		
		// 문자열 숫자 목록 스트림을 숫자 스트림으로 변환
		List<String> list2 = Arrays.asList("10, 20, 30", "40, 50");
		list2.stream()
			.flatMapToInt(data -> {
				String[] strArr = data.split(",");
				int[] intArr = new int[strArr.length];
				for(int i = 0; i < strArr.length; i++) {
					intArr[i] = Integer.parseInt(strArr[i].trim());
				}
				return Arrays.stream(intArr);
			})
			.forEach(number -> System.out.println(number));
	}
}

7. 요소 정렬

7.1. Comparable 구현 객체의 정렬
- 스트림의 요소가 객체일 경우, 객체가 Comparable을 구현한 상태여야 sorted() method를 사용하여 정렬이 가능
- 그렇지 않다면 ClassCastException 발생
 
 

- 내림차순으로 정렬하고 싶은 경우, Comparator.reverseOrder() method가 리턴하는 Comparator를 매개값으로 제공
 
Stream<Xxx> reverseOrderedStream = stream.sorted(Comparator.reverseOrder());
- 예제
package ch17.sec7.exam1;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
public class SortingExample {
	public static void main(String[] args) {
		// List collection 생성
		List<Student> studentList = new ArrayList<>();
		studentList.add(new Student("name1", 30));
		studentList.add(new Student("name2", 10));
		studentList.add(new Student("name3", 20));
		
		// 점수를 기준으로 오름차순으로 정렬한 새 스트림 얻기
		studentList.stream()
			.sorted()
			.forEach(s -> System.out.println(s.getName() + ": " + s.getScore()));
		
		System.out.println();
		
		// 점수를 기준으로 내림차순으로 정렬한 새 스트림 얻기
		studentList.stream()
			.sorted(Comparator.reverseOrder())
			.forEach(s -> System.out.println(s.getName() + ": " + s.getScore()));
	}
}

7.2. Comparator를 이용한 정렬
- 요소 객체가 Comparable을 구현하고 있지 않다면, 비교자를 제공하면 요소 정렬 가능
 - 비교자: Comparator interface를 구현한 객체
 
sorted((o1, o2) -> { ... })
- 예제
package ch17.sec7.exam2;
public class Student {
	private String name;
	private int score;
	
	public Student(String name, int score) {
		this.name = name;
		this.score = score;
	}
	
	public String getName() { return name; }
	public int getScore() { return score; }
}
package ch17.sec7.exam2;
import java.util.List;
import java.util.ArrayList;
public class SortingExample {
	public static void main(String[] args) {
		// List collection 생성
		List<Student> studentList = new ArrayList<>();
		studentList.add(new Student("name1", 30));
		studentList.add(new Student("name2", 10));
		studentList.add(new Student("name3", 20));
		
		// 점수 기준 오름차순 새 스트림 얻기
		studentList.stream()
			.sorted((s1, s2) -> Integer.compare(s1.getScore(), s2.getScore()))
			.forEach(s -> System.out.println(s.getName() + ": " + s.getScore()));
		
		System.out.println();
		
		// 점수 기준 오름차순 새 스트림 얻기
		studentList.stream()
			.sorted((s1, s2) -> Integer.compare(s2.getScore(), s1.getScore()))
			.forEach(s -> System.out.println(s.getName() + ": " + s.getScore()));
		
	}
}

8. 요소를 하나씩 처리(루핑)
- looping: 스트림에서 요소를 하나씩 반복해서 가져와 처리하는 것
 - looping methods peek(), forEach()
- peek(): 중간 처리 메소드, 최종 처리가 뒤에 붙지 않으면 동작 X
 - forEach(): 최종 처리 메소드
 
 

- 매개타입 Consumer는 함수형 인터페이스로, 아래와 같은 종류 존재
- accept(): 매개값을 처리(소비)
 
 

T -> { ... }
T -> 실행문;    // 하나의 실행문만 있을 경우 중괄호 생략
- 예제
package ch17.sec8;
import java.util.Arrays;
public class LoopingExample {
	public static void main(String[] args) {
		int[] intArr = { 1, 2, 3, 4, 5 };
		
		// 잘못 작성한 경우
		Arrays.stream(intArr)
			.filter(a -> a % 2 == 0)
			.peek(n -> System.out.println(n));	// 최종 처리가 없으므로 동작 X
		
		// 중간 처리 메소드 peek()을 이용하여 반복 처리
		int total = Arrays.stream(intArr)
				.filter(a -> a % 2 == 0)
				.peek(n -> System.out.println(n))
				.sum();	// 최종 처리
		System.out.println("sum: " + total);
		
		// 최종 처리 메소드 forEach()를 이용해서 반복 처리
		Arrays.stream(intArr)
			.filter(a -> a % 2 == 0)
			.forEach(n -> System.out.println((n)));	// 최종 처리이므로 동작함
	}
}

9. 요소 조건 만족 여부(매칭)
- Matching: 요소들이 특정 조건에 만족하는지 여부를 조사하는 최종 처리 기능
 


10. 요소 기본 집계
- Aggregate: 최종 처리 기능으로, 요소들을 처리해서 카운팅, 합계, 평균값, 최대값, 최소값 등과 같이 하나의 값으로 산출하는 것
 - 대량의 데이터를 가공해서 하나의 값으로 축소하는 reduction
 
10.1. 스트림이 제공하는 기본 집계

long count = Arrays.stream(arr)
    .filter(n -> n % 2 == 0)
    .average()
    .getAsDouble();
10.2. Optional 클래스
- Optional, OptionalDouble, OptionalInt, OptionalLong 클래스는 단순히 집계값만 저장하는 것이 아니라, 집계값이 존재하지 않을 경우 디폴트 값을 설정하거나 집계값을 처리하는 Consumer를 등록 가능
 - 컬렉션에 요소가 존재하지 않으면 집계 값을 산출할 수 없으므로 NoSuchElementException 예외 발생
- 예외 발생을 막는 3가지 방법
 
 

1) isPresent() method가 true를 리턴할 때만 집계값을 얻는다.
OptionalDouble optional = stream
    .average();
if(optional.isPresent()) {
    System.out.println("평균: " + optional.getAsDouble());
} else {
    System.out.println("평균: 0.0");
}
2) orElse() method로 집계값이 없을 경우를 대비해서 디폴트 값을 정해놓는다.
double avg = stream
    .average()
    .orElse(0.0);
System.out.println("평균: " + avg);
3) ifPresent() method로 집계값이 있을 경우에만 동작하는 Consumer 람다식을 제공
stream
    .average()
    .ifPresent(a -> System.out.println("평균: " + a));
11. 요소 커스텀 집계
- 스트림은 기본 집계 메소드인 sum(), average(), count(), max(), min()을 제공하지만, 다양한 집계 결과물을 만들 수 있도록 reduce() method도 제공
 

- 매개값인 BinaryOperator()는 함수형 인터페이스
 - apply(): 두 개의 매개값을 받아 하나의 값을 리턴
 
(a, b) -> { ... return 값 }
(a, b) -> 값    // return 문만 있을 경우 중괄호와 return 키워드 생략 가능
- reduce()는 스트림에 요소가 없을 경우 예외 발생, but identity 매개값이 주어지면 이 값을 디폴트로 리턴
 

int sum1 = studentList.stream()
                .mapToInt(Student :: getScore)
                .reduce(0, (a, b) -> a + b);    // .sum()과 동일
12. 요소 수집
- collect(): 요소들을 필터링 또는 매핑한 후 요소들을 수집하는 최종 처리 메소드
- 필요한 요소만 collection에 담을 수 있고, 요소들을 grouping한 후에 집계도 가능
 
 
12.1. 필터링한 요소 수집
- Stream의 collect(Collector<T, A, R> collector) method: 필터링 또는 매핑된 요소들을 새로운 컬렉션에 수집하고, 이 컬렉션을 리턴
 - Collector: 어떤 요소를 어떤 컬렉션에 수집할 것인지 결정
- T: 요소
 - A: 누적기(accumulator)
 - R: 요소가 저장될 collection
 - T 요소를 A 누적기가 R에 저장
 
 

- Collector의 구현 객체는 다음과 같이, Collectors class의 static method로 얻을 수 있음
- A(누적기)가 ?로 설정된 경우 -> Collector가 List, Set, Map collection에 요소를 저장하는 방법을 알고 있어 별도의 누적기 필요 X
 
 

List<Student> maleList = totalList.stream()
    .filter(s -> s.getSex().equals("남"))
    .collect(Collectors.toList());
    
Map<String, Integer> map = totalList.stream()
    .collect(
        Collectors.toMap(
            s -> s.getName(),    // Student 객체에서 키가 될 부분 리턴
            s -> s.getScore()    // Student 객체에서 값이 될 부분 리턴
        )
    );
Java 16부터는 좀 더 편리하게 요소 스트림에서 List collection을 얻을 수 있음 -> Stream의 toList() method
List<Student> maleList = totalList.stream()
    .filter(s -> s.getSex().equals("남"))
    .toList();
12.2. 요소 그룹핑
- collect() method 요소들을 그룹핑해서 Map 객체를 생성하는 기능 제공 -> Collectors.groupingBy() method에서 얻은 Collector를 collect() method를 호출할 때 제공
- T를 K로 매핑
 - K를 키로 해 List<T> 값으로 갖는 Map Collection 생성
 
 

Map<String, List<Student>> map = totalList.stream()
    .collect(
        Collectors.groupingBy(s -> s.getSex())    // grouping key return
    );
    
List<Student> maleList = map.get("남");
maleList.stream().forEach(s -> System.out.println(s.getName()));
System.out.println();
List<Student> femaleList = map.get("여");
femaleList.stream().forEach(s -> System.out.println(s.getName()));

- Collectors.groupingBy() method는 grouping 이후 매핑 및 집계(평균, 카운팅, 연결, 최대, 최소, 합계)를 수행할 수 있도록 두 번째 매개값인 Collector를 가질 수 있음
 - 다음은 두 번재 매개값으로 사용될 Collectors를 얻을 수 있는 Collectors의 static methods
 

Map<String, Double> map = totalList.stream()
    .collect(
        Collectors.groupingBy(
            s -> s.getSex(),
            Collectors.averagingDouble(s -> getScore())
        )
    );
    
System.out.println(map);

13. 요소 병렬 처리
- Parallel Operation: 멀티 코어 CPU 환경에서 전체 요소를 분할해서 각각의 코어가 병렬적으로 처리하는 것
 - java는 parallel operation을 위해 parallel stream을 제공
 
13.1. 동시성과 병렬성
- 동시성(Concurrency): multi-threads가 하나의 core에서 번갈아 가며 실행
 - 병렬성(Parallelism): multi-core를 각각 이용해서 병렬로 실행
- 데이터 병렬성: 전체 데이터를 분할해서 서브 데이터셋으로 만들고, 이 서브 데이터셋들을 병렬 처리해서 작업을 빨리 끝내는 것
 - 자바 병렬 스트림은 데이터 병렬성을 구현한 것
 - 작업 병렬성: 서로 다른 작업을 병렬 처리
 
 

13.2. 포크조인 프레임워크
- 자바 병렬 스트림은 요소들을 병렬처리하기 위해 ForkJoin Framework를 사용
 - ForkJoin Framework
- 포크 단계에서 전체 요소들을 서브 요소셋으로 분할
 - 각각의 서브 요소셋을 멀티 코어에서 병렬로 처리
 - 조인 단계에서 서브 결과를 결합해서, 최종 결과를 만들어냄
 
 

- ForkJoin Framework는 병렬 처리를 위해 ThreadPool을 사용
 - 각각의 코어에서 서브 요소셋을 처리하는 것은 worker thread이므로, thread 관리가 필요
 - ForkJoin Framework는 Executor의 구현 객체인 ForkJoinPool을 사용하여 worker thread 관리
 

13.3. 병렬 스트림 사용
- parallelStream(): collection(List, Set)을부터 병렬 스트림을 바로 리턴
 - parallel(): 기존 스트림을 병렬 처리 스트림으로 변환
 

- 예제
package ch17.sec13;
import java.util.*;
import java.util.stream.Stream;
public class ParallelExample {
	public static void main(String[] args) {
		Random random = new Random();
		
		// 1억 개의 Integer 객체 저
		List<Integer> scores = new ArrayList<>();
		for(int i = 0; i < 100000000; i++) {
			scores.add(random.nextInt(101));
		}
		
		double avg = 0.0;
		long startTime = 0;
		long endTime = 0;
		long time = 0;
		
		// 일반 스트림으로 처
		Stream<Integer> stream = scores.stream();
		startTime = System.nanoTime();
		avg = stream
				.mapToInt(i -> i.intValue())
				.average()
				.getAsDouble();
		endTime = System.nanoTime();
		time = endTime - startTime;
		System.out.println("avg: " + avg + ", 일반 스트림 처리 시간: " + time + "ns");
		
		Stream<Integer> parallelStream = scores.parallelStream();
		startTime = System.nanoTime();
		avg = parallelStream
				.mapToInt(i -> i.intValue())
				.average()
				.getAsDouble();
		endTime = System.nanoTime();
		time = endTime - startTime;
		System.out.println("avg: " + avg + ", 일반 스트림 처리 시간: " + time + "ns");
	}
}

13.4. 병렬 처리 성능
병렬 처리에 영향을 미치는 3가지 요인
1) 요소의 수와 요소당 처리 시간
- 컬렉션에 전체 요소의 수가 적고, 요소당 처리 시간이 짧으면 일반 스트림이 병렬 스트림보다 빠를 수 있음
- 병렬 처리는 포크 및 조인 단계 존재하고, threadPool을 생성하는 추가적 비용 발생하기 때문
 
 
2) 스트림 소스의 종류
- ArrayList와 배열: index로 요소를 관리하기 때문에, 포크 단계에서 요소를 쉽게 분리할 수 있어 병렬 처리 시간이 절약
 - HashSet, TreeSet, LinkedList: 요소 분리가 쉽지 않아, 상대적으로 병렬 처리가 늦음
 
3) 코어의 수
- CPU core의 수가 많을수록 병렬 스트림의 성능은 좋아짐
 - CPU core의 수가 적을 경우, 일반 스트림이 더 빠를 수 있음
- 병렬 스트림은 threads 수가 증가 -> 동시성(concurrency)이 많이 일어나므로, 오히려 느려짐
 
 
참고자료
728x90
    
    
  반응형
    
    
    
  'Programming Lang > Java' 카테고리의 다른 글
| [이것이 자바다] Chapter 19. 네트워크 입출력 (0) | 2025.02.07 | 
|---|---|
| [이것이 자바다] Chapter 18. 데이터 입출력 (1) | 2025.02.06 | 
| [이것이 자바다] Chapter 16. 람다식 (2) | 2025.02.02 | 
| [이것이 자바다] Chapter 15. 컬렉션 자료구조 (0) | 2025.02.02 | 
| [이것이 자바다] Chapter 14. 멀티 스레드 (1) | 2025.02.01 |