스트림(Stream)

Joy🌱·2023년 4월 5일
0

☕ Java

목록 보기
40/40
post-thumbnail

💁‍♀️ 스트림(Stream)이란,
컬렉션에 저장한 엘리먼트들을 하나씩 순회하면서 처리할 수 있는 기능.
자바 8 이전의 배열 또는 컬렉션을 다루는 방법은 'for', 'forEach'를 사용하여 엘리먼트를 꺼내서 다루는 방법이었음. 스트림을 이용하면 배열 또는 컬렉션을 함수 여러 개를 사용해서 결과를 쉽게 얻을 수 있으며, 람다식과 함께 사용하면 컬렉션에 담긴 데이터 처리를 간결하게 표현 가능

👀 스트림 ?

👉 스트림의 이용

asList()

List<String> stringList = Arrays.asList("Good", "to", "see", "you"); // asList() : 가변인자 전달 가능
		
/* 이전의 스트림을 이용하지 않던 방식 */
for(String str : stringList) {
	System.out.println(str);
}
		
/* 스트림을 이용한 방식 */
stringList.forEach(System.out::println); // 더 간결하지만 위와 같은 결과 출력
		

👉 스트림의 병렬 처리

currentThread() getName() forEach() parallelStream()

public static void main(String[] args) {
		
	/* 스트림의 병렬 처리 확인 */
	List<String> stringList = Arrays.asList("Hello", "my", "friend", "Joy");
		
	/* 스트림을 사용하지 않으면 모든 작업은 main 스레드에서 일어남 */
	System.out.println("================== forEach");
	for(String s : stringList) {
		System.out.println(s + " : " + Thread.currentThread().getName()); // currentThread().getName() : 현재 진행 중인 스레드의 이름 반환
	}
		
	/* 일반적인 스트림도 main 스레드가 작업을 수행 */
	System.out.println("================== normal stream");
	stringList.forEach(Application2::printThis);

	/* 병렬 스트림(parallelStream())을 사용하면 병렬 처리를 손쉽게 할 수 있음 */
	System.out.println("================== parallel stream");
	stringList.parallelStream().forEach(Application2::printThis);
	stringList.parallelStream().forEach(Application2::printThis);
	/* 출력된 순서가 다름 => 병렬적으로 처리되었기 때문 (작업 속도, 성능면에서 훨씬 우수) */
}
	
private static void printThis(String str) {
	System.out.println(str + " : " + Thread.currentThread().getName());
}

👀 스트림의 활용

👉 stream()으로 배열&컬렉션 스트림 생성

stream() asList()

/* 배열 스트림 생성 */
String[] sarr = {"Oh", "I", "got", "it"};
		
Stream<String> strStream1 = Arrays.stream(sarr); // 배열을 스트림으로 만들어 forEach로 출력
strStream1.forEach(System.out::println);

Stream<String> strStream2 = Arrays.stream(sarr, 0, 2); // 배열 뿐만 아니라 인덱스 값들을 전달 (start&end)
strStream2.forEach(System.out::println); // 0과 1번 인덱스인 "Oh", "I"만 출력
/* 컬렉션 스트림 생성 */
List<String> stringList = Arrays.asList("Make", "It", "Happen");
		
Stream<String> strStream3 = stringList.stream(); // 컬렉션을 스트림으로 만들어 forEach로 출력
strStream3.forEach(System.out::println);
		
/* forEach는 컬렉션에도 작성되어 있어 Stream으로 만들지 않고 사용 가능 */
stringList.forEach(System.out::println); // 위와 같은 결과

👉 기본 타입 스트림 생성

range(시작값, 종료값) : 시작값부터 1씩 증가하는 숫자로 종료값 전까지 범위의 스트림 생성
rangeClosed(시작값, 종료값) : 시작값부터 1씩 증가하는 숫자로 종료값까지 범위의 스트림 생성

IntStream intStream = IntStream.range(5, 10);
intStream.forEach(value -> System.out.print(value + " "));	// 5 6 7 8 9 
System.out.println();
		
LongStream longStream = LongStream.rangeClosed(5, 10);
longStream.forEach(value -> System.out.print(value + " "));	// 5 6 7 8 9 10 
System.out.println();

Wrapper 클래스자료형의 스트림으로 변환이 필요한 경우 boxing 가능
doubles(갯수) : 난수를 활용한 DoubleStream을 갯수 만큼 생성하여 반환
boxed() : 기본 타입 스트림인 XXXStream을 박싱하여 Wrapper 타입의 Stream<XXX>으로 반환

Stream<Double> doubleStream = new Random().doubles(5).boxed();
doubleStream.forEach(value -> System.out.print(value + " / "));
System.out.println();

문자열을 intStream으로 변환
chars()

IntStream helloworldChars = "I'm Joy".chars();
helloworldChars.forEach(v -> System.out.print(v + " "));	// 73 39 109 32 74 111 121 
System.out.println();

문자열을 split하여 stream으로 생성
Pattern.compile() splitAsStream()

Stream<String> splitStream = Pattern.compile(", ").splitAsStream("stand, on, my, own");
splitStream.forEach(System.out::println);

Stream.of()를 이용한 생성
concat()을 이용하여 두 개의 스트림을 동일 타입 스트림으로 합칠 수 있음

Stream<String> stringStream1 = Stream.of("Cheese", "is", "on", "fire🔥");
Stream<String> stringStream2 = Stream.of("He", "made", "it", "🌟");
		
//		stringStream1.forEach(System.out::println);
//		stringStream2.forEach(System.out::println);
		
/* 위 코드를 주석하지 않을 시, IllegalStateException 발생 */
Stream<String> concatStream = Stream.concat(stringStream1, stringStream2);
concatStream.forEach(v -> System.out.print(v + " "));

👀 스트림의 중간 연산 & 최종 연산

◼ StudentDTO class

public class StudentDTO {
	
	private int no;
	private int classNo;
	private String name;
	private int score;
    
    /* 생성자, getter&setter, toString */
}

👉 중간 연산

💁‍♀️ 중간 연산 ?
스트림을 전달 받아 또 다른 스트림을 반환하는 연산으로 연속으로 연결해서(chaining) 사용할 수 있음

  • Filtering, Mapping, Sorting 등의 가공 작업을 말함

filter() : 조건에 맞지 않는 요소 제거

IntStream stream1 = IntStream.rangeClosed(1, 10);

System.out.println("===== 짝수 값만 필터링 된 스트림 결과 출력 =====");
stream1.filter(i -> i % 2 == 0).forEach(System.out::print); // 246810
System.out.println();

map() : 스트림 요소 변환

Stream<String> stream2 = Stream.of("WOW", "It", "is", "difficult");

System.out.println("===== 문자열을 문자열의 길이로 변환한 스트림 결과 출력 =====");
stream2.map(s -> s.length()).forEach(System.out::print); // 3229
System.out.println();

sorted() : 정렬 Comparator를 전달하여 스트림 요소 정렬, Comparator를 전달하지 않을 시 기본 정렬(Comparable)로 정렬

Stream<String> stream3 = Stream.of("Cream", "Cheese", "is", "amazingly", "YUM!");

System.out.println("===== 정렬 된 문자 스트림 결과 출력 =====");
//	stream3.sorted().forEach(System.out::println); // 기본 정렬
//	stream3.sorted(Comparator.reverseOrder()).forEach(System.out::println); // 정렬 역순
	stream3.sorted(Comparator.comparing(String::length)).forEach(System.out::println); // 문자열 길이순 정렬

Stream<StudentDTO> stream4 = Stream.of(new StudentDTO(1, 3, "신짱구", 90), new StudentDTO(2, 1, "흰둥이", 80),
				new StudentDTO(3, 1, "신짱아", 85), new StudentDTO(4, 2, "봉미선", 95), new StudentDTO(5, 2, "신형만", 75));

System.out.println("===== 점수 기준으로 정렬 된 학생 스트림 결과 출력 =====");
stream4.sorted(Comparator.comparing(StudentDTO::getScore)).forEach(System.out::println);

👉 최종 연산

💁‍♀️ 최종 연산 ?
중간 연산을 통해 변환 된 스트림은 마지막으로 최종 연산을 통해 각 요소를 소모하여 결과를 표시. 지연 되었던 모든 중간 연산이 최종 연산 시에 수행.

forEach() : 스트림의 모든 요소를 소모하여 지정 된 작업을 수행 보통 스트림의 모든 요소를 출력하는 용도로 많이 사용

System.out.println("===== 스트림 요소 출력 =====");
IntStream.range(1, 10).forEach(System.out::println);
System.out.println();

reduce() : 스트림의 요소를 하나씩 줄여가며 누적 연산 수행

System.out.println("===== 요소 소모 결과 출력 =====");
IntStream stream1 = IntStream.rangeClosed(1, 50);
int sum = stream1.reduce(0, (a, b) -> a + b); // 1~50을 더한 결과
System.out.println(sum); // 1275

Stream<String> stream2 = Stream.of("apple", "banana", "cat", "dog");
/* java.util.Optional<T> 'T' 타입의 객체를 포장해주는 래퍼 클래스(Wrapper Class)로 모든 타입의 참조 변수를 저장 가능 */
Optional<String> result1 = stream2.reduce((s1, s2) -> s1 + " * " + s2);

/*
 * Optional 래퍼 클래스는 예상하지 못한 NullPoiniterException을 회피하기 위한 메소드를 제공
 * ifPresent()는 Optional 객체가 값을 가지고 있으면 실행하고 null인 경우 실행 X
 */
result1.ifPresent(System.out::println); // value가 있을 때만 실행

/* 인수로 초기 값을 전달하는 reduce 메소드의 반환 타입은 Optional<T>가 아니라 T 타입 */
Stream<String> stream3 = Stream.of("peach", "grape", "rabbit", "tiger");
String sum2 = stream3.reduce("⚡초기 값⚡", (s1, s2) -> s1 + " * " + s2);
System.out.println(sum2);

// 가장 스코어가 높은 학생 찾기
Stream<StudentDTO> stream4 = Stream.of(new StudentDTO(1, 3, "짱구", 90), new StudentDTO(2, 1, "철수", 80),
				new StudentDTO(3, 1, "유리", 85), new StudentDTO(4, 2, "훈이", 95), new StudentDTO(5, 2, "맹구", 75));

StudentDTO student = stream4.reduce((a, b) -> (a.getScore() > b.getScore()) ? a : b).get(); // a, b 매개변수를 통해 학생들이 한 명씩 걸러짐
System.out.println("가장 스코어가 높은 학생 : " + student);

count() : 요소의 개수 long 타입으로 반환
sum() : 요소의 합계 반환

IntStream stream5 = IntStream.of(99, 44, 55, 88, 77);
System.out.println(stream5.count()); // 5

IntStream stream6 = IntStream.of(99, 44, 55, 88, 77);
System.out.println(stream6.sum()); // 363
profile
Tiny little habits make me

0개의 댓글

관련 채용 정보