본문 바로가기
멀티캠퍼스 풀스택 과정/Java의 정석

자바의 정석11-3 스트림(Stream), 스트림 생성

by 이쟝 2022. 1. 16.

스트림(Stream)

- 다양한 데이터 소스(컬렉션, 배열, 임의의 수)를 표준화된 방법으로 다루기 위한 것

- 스트림: 데이터의 연속적인 흐름

 

스트림으로 변환(생성)

List로 형변환을 한 Arraylist를 각각의 타입으로 스트림 생성

 

<스트림 사용방법>

1) 스트림 만들기

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

3) 최종연산(1번) => 결과

 

(1) 스트림 생성

String[] strArr = {"aaa", "bbb", "ccc"};         // (2)문자열 배열

List<String> strList = Arrays.asList(strArr);  // (1)같은 내용의 문자열을 저장하는 List

              

(1) Stream<String> strStream1 = strList.stream();

// 스트림을 생성(collection안에 속해 있는 List 변환한 뒤에 stream()사용)

 

(2) Stream<String> strStream2 = Arrays.stream(strArr);

// 스트림을 생성(Arrays 사용해 stream 접근해서 strArr stream으로)

 

(2) 중간연산 (3) 최종연산

strStream1.sorted().forEach(System.out::println);  // (2), (3)

strStream2.sorted().forEach(System.out::println);  // (2), (3)

// 스트림을 sorted() 정렬(중간연산), forEach 화면에 출력(최종연산)

 

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

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

 

중간연산 연결

 

-> 중간 연산은 연산결과를 스트림으로 반환하기 때문에 중간 연산을 연속해서 연결할 수 있다. (점으로 연결해서 메서드를 사용하는 것을 뜻함 연산결과가 계속해서 스트림이기 때문에)

1) 스트림생성 2) 중간연산(distinct( ):중복 제거 / limit(5):5개 자르기 / sorted():정렬) 3) 최종연산 1(sysout 출력)

 


스트림의 특징

1) 스트림은 데이터 소스(원본)로부터 데이터를 읽기 만할 뿐 변경하지 않는다.(Read only)

 

 

더보기
더보기

1) 원본 List를 생성 

2) 원본 List를 중간연산으로 정렬한뒤 새로운 sortedList 생성

 

2) 스트림은 Iterator처럼 일회용이다.(필요시 다시 스트림 생성)

 

 

-> 최종연산이 되면 요소가 소모되기 때문에 기존의 Stream을 사용할 수 없음

 

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

 

 

-> 스트림에는 무한스트림유한스트림이 있다.

-> 스트림이 중간연산을 즉각적으로 하는게 아니라 스트림을 가지고 어떤 작업을 해야 하는 지 확인만 해 둠(중간연산 바로 실행 xx) -> 무한 스트림이기 때문

 

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

- 내부반복: 반복문을 메서드의 내부에 숨길 수 있다는 것을 의미함

 

 

-> forEach()는 for문을 아래 코드처럼 메서드 안에 넣어 버림(코드가 간결해짐)

 

 

5) 스트림의 작업을 병렬로 처리한다. 병렬스트림

 

 

-> 스트림에 .parallel( )이라는 메서드 호출 -> 병렬로 연산을 수행하도록 함

-> 반대로 병렬로 처리되지 않게 하려면 sequential( )(parallel()을 호출한 것을 취소할 때만 사용)

 

 

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

- 오토박싱&언박싱의 비효율이 제거됨(Stream<Integer>대신 IntStream사용: 데이터 소스가 기본형 일 때만)

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

-> Stream<Integer>: 스트림의 요소가 Integer

-> Stream변환 하면 기본형 -> 참조형으로 바뀌게 됨( 1 -> new Integer(1))

 


스트림만들기

1. 컬렉션

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

-> Collection의 자손인 ListSet을 구현한 컬렉션 클래스들은 모두 이 메서드로 스트림을 생성할 수 있다.

 

List<Integer>  list = Arrays.asList(1,2,3,4,5);  

Stream<Integer> intStream = list.stream();  // list 데이터 소스로 하는 새로운 스트림을 생성

              

// 스트림의 모든 요소를 출력

intStream.forEach(System.out::print); // 12345 forEach()최종연산

intStream.forEach(System.out::print); // 에러! stream has already been operated upon or Closed(스트림은 일회용이기 때문)

 

// 스트림은 일회용. 스트림에 대해 최종연산을 수행하면 stream 닫힌다.

intStream = list.stream();               // 스트림 다시 생성

intStream.forEach(System.out::print);  // 12345

 

2.배열

 

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

 

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) // from ~ to

 

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);

 

2) 기본형 배열로부터 스트림 생성하기

 

IntStream IntStream.of(int... values)  // Stream 아니라 IntStream

IntStream IntStream.of(int[])

IntStream Arrays.stream(int[])

IntStream Arryas.stream(int[] array, int startInclusive, int endExclusive)

 

예제)

 

Stream<String> strStream = Stream.of("a","b","c");
strStream.forEach(System.out::print); // abc
		
String[] strArr = {"A","B","C"};
Stream<String> strStream1 = Stream.of(strArr);
Stream<String> strStream2 = Stream.of(new String[] {"A","B","C"});
		
strStream1.forEach(System.out::print);  // ABC
strStream2.forEach(System.out::print);  // ABC
		
int[] intArr = {1,2,3,4,5};
IntStream intStream = Arrays.stream(intArr);  // 기본형스트림IntStream
intStream.forEach(System.out::print); // 12345
System.out.println("sum=" + intStream.sum());  // sum외에도 average등도 있음
//System.out.println("count=" + intStream.count());
		
Integer[] intArr1 = {1,2,3,4,5};
Stream<Integer> intStream1 = Stream.of(intArr1);  // Integer객체Stream
intStream1.forEach(System.out::print);  //  12345

 

3. 임의의 수

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

- 이 메서드들이 반환하는 스트림은 크기가 정해지지 않은 무한스트림(infinite stream)’이므로 limit()도 같이 사용해서 스트림의 크기를 제한해 주어야 한다.

-> limit()은 스트림의 개수를 지정하는데 사용되며, 무한스트림을 유한스트림으로 만들어준다.

 

IntStream    intStream = new Random().ints(); // 무한 스트림

intStream.limit(5).forEach(System.out::println);  // 5개의 요소(중간연산)만 출력(최종연산)한다.

 

예제) 

 

IntStream intStream1 = new Random().ints();  // 크키가 5인 난수 스트림 반환
intStream1.limit(5).forEach(System.out::println);  // 무한스트림에서 5개의 요소 출력
		
IntStream intStream2 = new Random().ints(10,1,10); // 크기가 10인 1~9까지의 난수 생성
intStream2.forEach(System.out::println);
		
IntStream intStream3 = new Random().ints(1,10);    // 1~9까지의 난수 생성
intStream3.limit(10).forEach(System.out::println); // 크기를 10으로 설정

 

4. 특정 범위의 정수

 

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

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

IntStream intStream1 = IntStream.rangeClosed(1, 5); // 1,2,3,4,5 끝이 포함

 

5. 람다식 iterate( ), generate( )

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

- generate( ) Iterate( )처럼, 람다식에 의해 계산되는 값을 요소로 하는 무한 스트림을 생성(limit 값으로 잘라줘야 함)

 

 

-> iterate( )는 이전 요소를 seed(초기값)로 해서 다음 요소를 계산한다.

-> generate()seed(초기값)를 사용하지 않는다.

 

//iterate(T Seed, UnaryOperator f) 단항 연산자(값을 하나 넣으면 하나 나옴)
Stream<Integer> intStream1 = Stream.iterate(2, n -> n + 2);  // 2부터 짝수스트림(10개)
intStream1.limit(10).forEach(System.out::println);
	
Stream<Integer> intStream2 = Stream.iterate(1, n -> n+2); //1부터 홀수스트림(10개)
intStream2.limit(10).forEach(System.out::println);

// generate(Suppliers s): 주기만 하는 것 입력 x 출력O
Stream<Integer> oneStream = Stream.generate(()->1);  // 1만 10번 출력
oneStream.limit(10).forEach(System.out::println);

 

6. 파일과 빈 스트림

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

 

 

- 비어있는 스트림 생성하기

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

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