스트림(Stream) 🌊

- 다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것

※ 데이터 소스 : 컬렉션, 배열

 

컬렉션 프레임워크는 List, Set, Map 하고 있는 데 사용 방법이 다 같지는 않았다.

스트림은 JDK 1.8부터 통일됨.

 

List, Set, Map으로 Stream을 만들 수 있다.

Stream을 만들고 나면 다 똑같다.

Stream -> 중간 연산 -> 최종 연산 -> 결과

 

  • List <Integer> list = Arrays.asList(1,2,3,4,5);
  • Stream <Integer> intStream = list.stream(); // 컬렉션
  • Stream <String> strStream = Stream.of(new String []{"a", "b", "c"}); // 배열
  • Stream <Integer> evenStream = Stream.iterate(0, n->n+2); // 0,2,4,6...
  • Stream <Double> randomStream = Stream.generate(Math::random); // 람다식
  • IntStream intStream = new Random(). ints(5); // 난수 스트림 (크기가 5)
스트림이 제공하는 기능 

- 중간 연산 : 연산 결과가 스트림인 연산, 반복적으로 적용 가능

- 최종 연산 : 연산 결과가 스트림이 아닌 연산. 단 한 번만 적용 가능(스트림의 요소를 소모)

 

1. 스트림 만들기

2. 중간 연산(0~n번)

3. 최종 연산(0~1번)

 

stream.distinct(). limit(5). sorted(). forEach(System.out::println)

            중간 연산 3번                    최종 연산 1번


스트림(Stream)의 특징

- 스트림은 데이터 소스로부터 데이터를 읽기만 할 뿐 변경하지 않는다.

List<Integer> list = Arrays.asList(3,1,5,4,2);
List<Integer> sortedList = list.stream().sorted()   // list를 정렬해서
		.collect(Collectors.toList());    // 새로운 List에 저장

list = [3 , 1 , 5 , 4 , 2]

sortedList = [1 , 2 , 3 , 4 , 5]

변경되지 않았음.

 

- 스트림은 Iterator처럼 일회용이다. (필요하면 다시 스트림을 생성해야 함)

strStream.forEach(System.out::println); // 모든 요소를 화면에 출력 (최종연산)
int numOfStr = strStream.count();       // 에러. 스트림이 이미 닫혔음.

 

- 최종 연산 전까지 중간 연산이 수행되지 않는다. - 지연된 연산

IntStream intStream = new Random().ints(1,46); // 1~45범위의 무한 스트림
intStream.distinct().limit(6).sorted()         // 중간 연산
   .forEach(i->System.out.print(i+","));       // 최종 연산

로또 번호 생성하는 코드임

로또 번호 중복 들어가면 안 되니깐 distinct() 그리고 숫자는 6개 잘라줘야 하므로 limit() 정렬 sorted()

중복을 제거하고 


스트림(Stream)의 특징 2

- 스트림은 작업을 내부 반복으로 처리한다.

// 반복문
for(String str : strList)
	System.out.println(str);

// 스트림 사용
stream.forEach(System.out::println);

forEach(최종 연산) 문을 열어보면 for문이 안에 들어가 있음

성능은 좋지 않지만 코드가 간결해진다는 장점이 있음.

void forEach(Consumer<? super T> action) {
	Objects.requireNonNull(action); // 매개변수의 널 체크
    
    for(T t : src) // 내부 반복 (for문을 메서드 안으로 넣음)
    	action.accept(T);
}

- 스트림의 작업을 병렬로 처리 - 병렬 스트림

Stream<String> strStream = Stream.of("dd","aaa","CC","CC","b");
int num = strStream.parallel() // 병렬 스트림으로 전환 (속성만 변경)
		.mapToInt(s -> s.length()).sum(); // 모든 문자열의 길이의 합

멀티 스레드로 병렬 처리한 것.

빅데이터는 한 개의 스레드보다 여러 개의 스레드가 처리하는 것이 좋음.

병렬로 처리하는 것이 parallel() 메서드이다.

 

- 기본형 스트림 - IntStream, LongStream, DoubleStream

 - 오토 박싱&언박싱의 비효율이 제거됨(Stream <Integer> 대신 IntStream 사용)

 - 숫자와 관련된 유용한 메서드를 Stream <T>보다 더 많이 제공


스트림 만들기 - 컬렉션

- Collection인터페이스의 stream()으로 컬렉션을 스트림으로 변환

	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(1,2,3,4,5);
		Stream<Integer> intStream = list.stream(); // list를 Stream으로 변환
		intStream.forEach(System.out::print);
		intStream.forEach(System.out::print);
	}

출력 결과 에러 났음

에러난 이유는 스트림은 일회용인데 두 번 사용했기 때문!

		List<Integer> list = Arrays.asList(1,2,3,4,5);
		Stream<Integer> intStream = list.stream(); // list를 Stream으로 변환
		intStream.forEach(System.out::print);
		
		intStream = list.stream();
		intStream.forEach(System.out::print);

이렇게 하나를 더 만들어 줘야 한다. (완전 iterator 하고 똑같네..)

 

스트림을 배열로 만들어보자

- 객체 배열로부터 스트림 생성하기

Stream <T> stream.of(T..values) // 가변 인자

Stream<T> stream.of(T [])

Stream <T> Arrays.stream(T [])

Stream <T> Arrays.stream(T [] array, int startInclusive, int endExclusive) 배열의 일부로 만드는 것

사용 예

Stream<String> strStream = Stream.of("a","b',"c");
Stream<String> strStream = Stream.of(new String[]{"a","b","c"});
Stream<String> strStream = Arrays.stream(new String[]{"a","b","c"});
Stream<String> strStream = Arrays.stream(new String[]{"a","b","c"}, 0, 3);

한번 사용해보자.

		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
		Stream<Integer> intStream2 = list.stream(); // list를 Stream으로 변환
		intStream2.forEach(System.out::print); // forEach()최종연산

		// stream 1회용. stream에 대해 최종연산을 수행하면 stream이 닫힌다.
		intStream2 = list.stream(); // list로부터 stream을 생성
		intStream2.forEach(System.out::print);

		String[] strArr = { "a", "b", "c", "d" };
//		Stream<String> strStream = Stream.of(new String[] {"a","b","c","d"});
		Stream<String> strStream = Arrays.stream(strArr);
		strStream.forEach(System.out::println);

		int[] intArr = { 1, 2, 3, 4, 5 };
		Arrays.stream(intArr);
		IntStream intStream = Arrays.stream(intArr);
//		intStream.forEach(System.out::println);
//		System.out.println("count="+intStream.count());
//		System.out.println("sum="+intStream.sum());
		System.out.println("average="+intStream.average());

		Integer[] intArr2 = { 1, 2, 3, 4, 5 };
		Arrays.stream(intArr2);
		Stream<Integer> intStream3 = Arrays.stream(intArr2);
		intStream3.forEach(System.out::println);

기본형 IntStream 같은 경우는 count, sum, average를 제공을 해준다.

count 개수 sum 합계 average 평균

근데 객체 Stream은 숫자인지 문자인지 모르기 때문에 sum, average 같은 숫자형 메서드는 없다.

average 사용시 OptionalDouble[3.0] 출력


스트림 만들기 - 임의의 수

- 난수를 요소로 갖는 스트림 생성하기

IntStreamintStream = new Random().ints(); // 무한 스트림
intStream.limit(5).forEach(System.out::println); // 5개의 요소만 출력한다.

IntStream intStream = new Random().ints(5); // 크기가 5인 난수 스트림을 반환

스트림에는 유한 스트림, 무한 스트림이 존재한다.

무한 스트림을 생성해서 5개를 잘라준다.

 

밑에 코드는 크기가 5인 난수 스트림을 반환하는 것.

	//IntStream intStream = new Random().ints();
	IntStream intStream = new Random().ints(1,45);
	intStream.limit(6).forEach(System.out::println);

. limit(5) 없으면 계속 나옴 무한 스트림이라..

. ints(5,10) 범위를 지정해 줄 수도 있음.. 그럼 5~10의 값이 나오고 1,45를 하면 로또 번호 1~45를 만들 수 있음

distinct() 메서드를 추가시키면 중복도 없애겠지?

 

- 특정 범위의 정수를 요소로 갖는 스트림 생성하기

IntStream intStream = IntStream.range(1, 5);		// 1,2,3,4
IntStream intStream = IntStream.rangeClosed(1, 5);  // 1,2,3,4,5

closed 포함 없는 건 그냥 ~


스트림 만들기 - 람다식 iterate(), generate()

- 람다식을 소스로 하는 스트림 생성

iterate는 종속적 , generate는 독립적

- iterate()는 이전 요소를 seed로 해서 다음 요소를 계산함.

Stream <Integer> evenStream = Stream.iterate(0, n->n+2); // 0, 2, 4, 6 ,...

n -> n+2면

0 -> 0+2

2 -> 2+2

4 -> 4+2

- generate()는 seed를 사용하지 않는다.

Stream <Double> randomStream = Stream.generate(Math::random);

Stream <Integer> oneStream = Stream.generate(()->1);

Stream<Integer> intStream = Stream.iterate(0, n -> n+2);
intStream.limit(10).forEach(System.out::println);

결과

Stream<Integer> oneStream = Stream.generate(()->1);
oneStream.limit(10).forEach(System.out::println);

결과

리미트가 없으면 계속 찍힘.

generate(Supplier s) : 입력은 없고 출력만 있음. 주기만 한다.


스트림 만들기 - 파일과 빈 스트림

- 파일을 소스로 하는 스트림 생성하기

Stream <Path> Files.list(Path dir) // Path는 파일 또는 디렉터리

 

Stream <String> Files.lines(Path path)

Stream<String> Files.lines(Path path, Charset cs)

Stream<String> lines() // BufferdReader클래스의 메서드

 

파일의 내용을 라인 단위로 읽어서 스트림에 넣어줄 수 있다.

 

- 비어있는 스트림 생성

Stream emptyStream = Stream.empty(); // empty()는 빈 스트림을 생성해서 반환한다.

long count = emptyStream.count(); // count의 값은 0

 

+ Recent posts