Notice
Recent Posts
Recent Comments
관리 메뉴

Developer Gonie

2. 스트림 (Java 8부터 추가됨)*** => 알고리즘 문제에 활용가능 본문

인프런 김영한님 강의/0. 수강중 Java 추가 공부

2. 스트림 (Java 8부터 추가됨)*** => 알고리즘 문제에 활용가능

이대곤 2022. 6. 25. 20:40

스트림(Java 8부터 추가됨)

* 스트림이란?

Stream 반복자는 Iterator 반복자와 비슷하게 컬렉션(배열포함)의 저장 요소를 하나씩 참조하는 비슷한 역할을 하지만 다음과 같은 다른점을 가지고 있다.(여기서 말하는 컬렉션은 List, Set 인터페이스의 부모 인터페이스 Collection의 모든 자식들을 의미하는 것 같다. 애초에 Iterator를 사용할 수 있는 것들도 Collection 인터페이스의 자식들이었음)

 

스트림은

1) 하나씩 참조한 요소를 람다식으로 처리할 수 있도록 한다.

2) 내부 반복자를 사용하므로 병렬 처리가 쉽다.

3) 중간 처리와 최종 처리 작업을 수행할 수 있다.

 

각각의 다른점에 대한 얘기는 아래서 살펴보자.

Java 8부터 Iterator 반복자를 대신해 사용할 수 있다.

List<String> 컬렉션의 모든 원소 출력, Iterator, Stream 구현코드 비교

반복자라는 점에서 요소를 하나씩 가져오는 공통점이 있음

List<String> list = Arrays.asList("홍길동", "신용권", "김자바");

// 1. Iterator 반복자로 모든 원소를 출력하는 코드
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
	String name = (String) iterator.next();
	System.out.println(name);
}

// 2. Stream 반복자를 이용해 모든 원소를 출력하는 코드        
Stream<String> stream = list.stream();
stream.forEach(name -> System.out.println(name));

// 공통된 출력결과
홍길동
신용권
김자바

1) "하나씩 참조한 요소를 람다식으로 처리할 수 있도록 한다."의 의미

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class kakaka {
	public static void main(String[] args) {

		List<Student> list = Arrays.asList(
        		new Student("홍길동", 90),
			new Student("이신한", 82)
                );

		Stream<Student> stream = list.stream();
		stream.forEach(s -> {		// 요소를 하나씩 가져와 람다식으로 처리
			String name = s.getName();
			int score = s.getScore();
			System.out.println(name + "-" + score);
		});
	}
}

class Student{
	private String name;
	private int score;
	
	public Student(String name, int score) {
		super();
		this.name = name;
		this.score = score;
	}
	
	public String getName() { return name; }

	public int getScore() { return score; }
}

2) "내부 반복자를 사용하므로 병렬 처리가 쉽다."의 의미

* 외부 반복자(external iterator)란?

개발자가 코드로 직접 컬렉션의 요소를 반복해서 가져오는 코드 패턴을 말한다.

index를 이용한 for문, Iterator를 이용하는 while문 모두 외부 반복자를 이용하는 것이다.

 

* 내부 반복자(internal iterator)란? -> 스트림 방식

컬렉션 내부에서 요소들을 반복시키는 것이 자동으로 이뤄지고, 개발자는 요소당 처리해야 할 코드만 제공하는 코드 패턴을 말한다. 이를통해 얻는 이점은 컬렉션 내부에서 어떻게 요소를 반복시킬 것인가는 컬렉션에게 맡겨두고, 개발자는 요소 처리에만 집중할 수 있다는 것이다.

 

스트림에는 '순차처리 스트림'과 '병렬처리 스트림'이 존재하는데

각각 컬렉션의 stream(), parallelStream() 메소드를 이용해 생성하며

병렬처리 스트림의 경우 하나의 작업을 서브 작업들로 자동으로 나누고,

서브 작업의 결과물들을 자동으로 결합해서 최종 결과물을 생성한다. 

아래서 병렬처리 스트림을 이용한 경우 main 스레드를 포함해서

ForkJoinPool(스레드풀)의 작업 스레드들이 병렬적으로 요소를 처리하는 것을 볼 수 있다.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class kakaka2 {
	public static void main(String[] args) {
		List<String> list = Arrays.asList("홍길동", "용신권", "감바자", "람다고", "박병식");

		// 순차처리 스트림 이용
		Stream<String> stream = list.stream();
		stream.forEach(kakaka2 :: print); // 매개변수가 한 개 일 때는 파라미터 받는 것도 생략가능 한가봄 
		
		System.out.println();
		
		// 병렬처리 스트림 이용
		Stream<String> stream2 = list.parallelStream();
		stream2.forEach(kakaka2 :: print);
	}
	
	public static void print(String str) {
		System.out.println(str + " : " + Thread.currentThread().getName());
	}
}

/*
홍길동 : main
용신권 : main
감바자 : main
람다고 : main
박병식 : main

감바자 : main
람다고 : ForkJoinPool.commonPool-worker-3
홍길동 : ForkJoinPool.commonPool-worker-3
박병식 : main
용신권 : ForkJoinPool.commonPool-worker-5
*/

3) "중간 처리와 최종 처리 작업을 수행할 수 있다."의 의미

* 중간 처리 : 매핑, 필터링, 정렬을 수행하고

* 최종 처리 : 반복, 카운팅, 평균, 총합 등의 집계 처리를 수행한다.

 

매핑 : 스트림의 요소를 다른 요소로 대체하는 작업을 말한다.

필터링 : 요소를 걸러내는 역할을 한다. distinct()와 filter() 메소드는 모든 스트림이 가지고 있다.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class kakaka3 {
	public static void main(String[] args) {

		List<Member> list = Arrays.asList(
        		new Member("홍길동", Member.MALE, 30),
			new Member("김나리", Member.FEMALE, 20),
			new Member("신용권", Member.MALE, 45),
			new Member("박수미", Member.FEMALE, 27) 
                );

		double ageAvg = list.stream()
				.filter(m -> m.getSex() == Member.MALE) // 중간 처리로 남자만 필터링
				.mapToInt(Member :: getAge) // 중간 처리로 객체에서 나이값만 가져와 요소를 대체
				.average() // 최종 처리로 평균 집계
				.getAsDouble();
		
		int cnt = (int) list.stream()
			.filter(m -> m.getSex() == Member.MALE) // 중간 처리로 남자만 필터링
			.mapToInt(Member :: getAge) // 중간 처리로 객체에서 나이값만 가져와 요소를 대체
			.count();
		
		System.out.println("남자 평균 나이: " + ageAvg);
		System.out.println("남자: " + cnt + "명");
	}
}

class Member{
	public static int MALE = 0;
	public static int FEMALE = 1;
	
	private String name;
	private int sex;
	private int age;
	
	public Member(String name, int sex, int age) {
		super();
		this.name = name;
		this.sex = sex;
		this.age = age;
	}

	public int getSex() { return sex; }

	public int getAge() { return age; }
}

 

 

Comments