본문 바로가기

BackEnd/Java

[java] 이것이 자바다 ch17 스트림(내부 반복자, 파이프라인, 인터페이스)

728x90
반응형

1. 스트림

   ▷ Java8부터 컬렉션 및 배열의 요소를 반복 처리하기 위해 스트림 사용

   ▷ 요소들이 하나씩 흘러가면서 처리된다는 의미

   ▷ List 컬렉션의 stream() 메소드로 Stream 객체를 얻고, forEach() 메소드로 요소를 어떻게 처리할지 람다식으로 제공

   ▷ 스트림과 Iterator 차이점

      ▶ 내부 반복자이므로 처리 속도가 빠르고 병렬 처리에 효율적

      ▶ 람다식으로 다양한 요소 처리를 정의

      ▶ 중간 처리와 최종 처리를 수행하도록 파이프 라인을 형성

 

◎ 스트림 사용 예제

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Stream;

public class StreamExample {

	public static void main(String[] args) {
		// Set 컬렉션 생성
		Set<String> set = new HashSet<>();
		set.add("홍길동");
		set.add("신용권");
		set.add("감자바");
		
		// Iterator 사용 : set 이용시 많이 사용 방법
		Iterator<String> iterator = set.iterator();
		while(iterator.hasNext()) {
			String item = iterator.next();
			System.out.println(item);
		}
		System.out.println();

		// Stream을 이용한 요소 반복 처리
		Stream<String> stream = set.stream();
		stream.forEach(name -> System.out.println(name));
	}
}

//	출력 : 
//	홍길동
//	신용권
//	감자바
//	
//	홍길동
//	신용권
//	감자바


▷ Iiterator는 하나씩 불러와서 저장하는 것이라면 Stream은 연결해서 불러오는 것이라고 생각하면 됩니다.

 

 

2. 내부 반복자

   ▷ 요소 처리 방법을 컬렉션 내부로 주입시켜서 요소를 반복 처리

   ▷ 개발자 코드에서 제공한 데이터 처리 코드(람다식)를 가지고 컬렉션 내부에서 요소를 반복 처리

   ▷ 내부 반복자는 멀티 코어 CPU를 최대한 활용학 위해 요소들을 분배시켜 병렬 작업 가능

 

◎ 내부 반복자 사용 예제(스레드 사용)

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

public class ParallelStreamExample {
	public static void main(String[] args) {
		// List 컬렉션 생성
		List<String> list = new ArrayList<>();
		list.add("홍길동");
		list.add("신용권");
		list.add("감자바");
		list.add("람다식");
		list.add("박병렬");
		
		// 기존 방식
		for(int i=0; i<list.size(); i++) {
			String item = list.get(i);
			System.out.println(item);
		}
		System.out.println();
		
		// 병렬 처리
		Stream<String> parallelStream = list.parallelStream();
		parallelStream.forEach(name -> {
			System.out.println(name + ": " + Thread.currentThread().getName());
		});
	}
}

//	출력 : 
//	홍길동
//	신용권
//	감자바
//	람다식
//	박병렬
//	
//	감자바: main
//	람다식: main
//	홍길동: main
//	박병렬: ForkJoinPool.commonPool-worker-2
//	신용권: ForkJoinPool.commonPool-worker-1


▷ currentThread는 실행할때마다 현재 스레드명이 바뀝니다.
 내부 반복자는 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있습니다.
 하나씩 처리하는 순차적 외부 반복자보다는 효율적으로 요소를 반복시킬 수 있습니다.

 

 

3. 스트림 파이프라인

   ▷ 컬렉션의 오리지널 스트림 뒤에 필터링 중간 스트림이 연결될 수 있고, 그 뒤에 매핑 중간 스트림이 연결될 수 있음

   ▷ 오리지널 스트림과 집계 처리 사이의 중간 스트림들은 최종 처리를 위해 요소를 걸러내거나(필터링), 요소를 변환시키거나(매핑), 정렬하는 작업을 수행

   ▷ 최종 처리는 중간 처리에서 정제된 요소들을 반복하거나, 집계(카운팅, 총합, 평균) 작업을 수행

 

◎ 파이프라인 사용 예제

1. Student 클래스
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; }
}​

 

2. StreamPipeLineExample 메인 클래스
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamPipeLineExample {
	public static void main(String[] args) {
		List<Student> list = Arrays.asList(
			new Student("홍길동", 10),
			new Student("신용권", 20),
			new Student("유미선", 30)
			);
		
		// 방법1
		/*
		Stream<Student> studentStream = list.stream();
		// 중간 처리(학생 객체를 점수로 매핑)
		IntStream scoreStream = studentStream.mapToInt(student -> student.getScore());
		// 최종 처리(평균 점수)
		double avg = scoreStream.average().getAsDouble();
		 */
		
		// 방법2
		double avg = list.stream()
				.mapToInt(stu -> stu.getScore())
				.average()
				.getAsDouble();
		
		System.out.println("평균 점수: " + avg);
	}
}

//	출력 : 
//	평균 점수: 20.0​

▷ stream은 list를 쭉 연결해서 가져오고 mapping 되어있는 값 중 int 값을 찾고 평균을 구합니다.

 

 

4. 리소스로부터 스트림 얻기

◎ 스트림 인터페이스

   ▷ java.util.stream 패키지에는 BaseStream 인터페이스를 부모로 한 자식 인터페이스들은 상속 관계

   ▷ BaseStream에는 모든 스트림에서 사용할 수 있는 공통 메소드들이 정의

NO 리턴 타입 메소드(매개변수) 소스
1 Stream<T> java.util.Collection.stream()
java.util.Collection.parallelStream()
List 컬렉션
Set 컬렉션
2 Stream<T>
IntStream
LongStream
DoubleStream
Arrays.stream(T[ ]),             Stream.of(T[ ])
Arrays.stream(int[ ]),            IntStream.of(int[ ])
Arrays.stream(long[ ]),         LongStream.of(long[ ])
Arrays.stream(double[ ]),     DoubleStream.of(double[ ])
배열
3 IntStream IntStream.range(int, int)
IntStream.rangeClosed(int, int)
int 범위
4 LongStream LongStream.range(long, long)
LongStream.rangeClosed(long, long)
long 범위
5 Stream<Path> Files.list(Path) 디렉토리
6 Stream<String> Files.lines(Path, Charset) 텍스트 파일
7 DoubleStream
IntStream
LongStream
Random.doubles(...)
Random.ints()
Random.longs()
랜덤 수

 

◎ 컬렉션으로부터 스트림 얻기

   ▷ java.util.Collection 인터페이스는 스트림과 parallelStream() 메소드를 가지고 있어

       자식 인터페이스인 List와 Set 인터페이스를 구현한 모든 컬렉션에서 객체 스트림을 얻을 수 있습니다.

 

1. Product 클래스
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();
	}
}​


2. StreamExample 메인 클래스

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

public class StreamExample {

	public static void main(String[] args) {
		// List 컬렉션 생성
		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));
	}
}

//	출력 : 
//	{pno:1,name:상품1,company:멋진 회사 ,price:3848}
//	{pno:2,name:상품2,company:멋진 회사 ,price:2207}
//	{pno:3,name:상품3,company:멋진 회사 ,price:5000}
//	{pno:4,name:상품4,company:멋진 회사 ,price:2853}
//	{pno:5,name:상품5,company:멋진 회사 ,price:5950}

 

◎ 배열로부터 스트림 얻기

   ▷ java.util.Arrays 클래스로 다양한 종류의 배열로부터 스트림을 얻을 수 있습니다.

 

package stream3;

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 = {"홍길동", "신용권", "김미나"};
		System.out.println("String 배열 내용 출력");
		Stream<String> strStream = Arrays.stream(strArray);
		strStream.forEach(item -> System.out.print(item + ","));
		System.out.println();
		System.out.println();
		
		int[] intArray = { 1, 2, 3, 4, 5 };
		System.out.println("Int 배열 내용 출력");
		IntStream intStream = Arrays.stream(intArray);
		intStream.forEach(item -> System.out.print(item + ","));
	}
}
	
//	출력 : 
//	String 배열 내용 출력
//	홍길동,신용권,김미나,
//	
//	Int 배열 내용 출력
//	1,2,3,4,5,

 

◎ 숫자 범위로부터 스트림 얻기

   ▷ IntStream 또는 LongStream의 정적 메소드인 range()와 rangeClosed() 메소드로 특정 범위의 정수 스트림을 얻을 수 있습니다.

 

range(1, 10);               1~9   : 끝 수를 포함하지 않는다.
rangeClosed(1, 10);    1~10 : 끝 수를 포함한다.

 

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 = 0;
		
		IntStream stream1 = IntStream.range(1, 100);
		stream1.forEach(a -> sum += a);
		System.out.println("총합: " + sum);
	}
}

//	출력 : 
//	총합: 5050
//	총합: 4950

 

◎ 파일로부터 스트림 얻기

   ▷ java.nio.file.Files의 lines() 메소드로 텍스트 파일의 행 단위 스트림을 얻을 수 있습니다.

 

1. data.txt
{"pno":1,"name":"상품1","company":"멋진 회사" ,"price":1357}
{"pno":2,"name":"상품2","company":"멋진 회사" ,"price":2057}
{"pno":3,"name":"상품3","company":"멋진 회사" ,"price":6995}
{"pno":4,"name":"상품4","company":"멋진 회사" ,"price":1843}
{"pno":5,"name":"상품5","company":"멋진 회사" ,"price":3863}​


▷ key와 value가 있는 json이나 map 형식과 비슷합니다.

2. StreamExample 메인 클래스
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();
	}
}​

결과 값은 데이터 파일과 동일하게 출력됩니다.

 

스트림에 대해서 시작해보았는데요 Iterator와 비슷한 것 같죠?

 

다음 강의에서 좀 더 알아볼게요~!!

 

많은 분들의 피드백은 언제나 환영합니다! 많은 댓글 부탁드려요~~

 

728x90
반응형