본문 바로가기

JAVA/JAVA

스트림(Stream) - 개념(1/3)

728x90

1. 개념

스트림은 데이터의 연속적인 흐름. 다양한 데이터(컬렉션 배열 등)를 표준화된 방법으로 다룰 수 있다. 

 

List<Integer> list = Arrays.asList(5,12,13,57);

위의 코드는 Integer 요소가 있는 list를 표현한 것이다. 형태가 list로 고정됐다.

Stream<Integer> intStream = list.stream(); // 컬렉션
Stream<String> strStream = stream.of(new String[]{"apple","cd","melon"}); //배열
Stream<Integer> intStream = stream.iterate(0,n->n+3); //0,3,6,9
Stream<Double> intStream = stream.generate(Math::random); //람다식

반면 스트림은 어떤 형태든 변화할 수 있다. 

 

2. 연산

스트림을 이용한 연산은 총 3단계이다. 

출처:https://medium.com/@knoldus/what-are-streams-api-in-java-8-how-its-unique-281dcc405e9b

  1. 처음에 List, Set, Map 등과 같은 컬렉션을 받는다.
  2. N번의 중간 연산을 한다. 
  3. 1번의 최종연산으로 결과가 나온다.

 

중간 연산은 연산결과가 스트림인 연산으로 반복적으로 적용가능하다. 반면 최종 연산은 연산결과가 스트림이 아닌 연산으로 한번만 적용가능하다.

 

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

 

위의 연산에서 중간연산과 최종 연산을 나눠보면 아래와 같다. 

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

주황색이 중간연산을 나타내고 빨간색이 최종연산을 나타낸다. 

 

중간연산과 최종연산에 대한 자세한 내용은 2, 3편에 소개하겠다. 

 

3. 특징

1) 스트림은 읽기만하지 변경하지 않는다. 원본은 변경을 하지 않는다. 

List<Integer> list = Arrays.asList(3,1,5,4,2);
List<Integer> sortedList = list.stream().sorted().collect(Collectors.toList());

System.out.println(list);	//[3,1,5,4,2]
System.out.println(sortedList);	//[1,2,3,4,5]

위의 코드를 보면 list는 변하지 않았다. 대신 stream으로 정렬된 배열을 얻었다. stream으로 작업하는 건 원본을 건드리지 않는 것이다. 

 

2) Iterator처럼 일회용이다.

strStream.forEach(System.out.println) //모든 요소를 화면에 출력한다. 
int nums = strStream.count()	//에러. 스트림이 이미 닫혀있다.

 

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

IntStream intStream = new Random().ints(1,31); //1~30범위.
IntStream.distinct().limit(6).sorted() // 중간 연산
				.forEach(i->System.out.println(i+","));  // 최종연산

distinct로 중복 제거하고 limit로 6개를 받고 sorted로 정렬했다. 원래는 이런 연산이 안 되지만 Stream은 지연된 연산이 가능하기에 가능한 것이다. 

 

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

for(String str: strList)
	System.out.println(str);

위의 코드를

stream.forEach(System.out.println);

stream으로 간결하게 표현할 수 있다. 

 

5) 병렬로 처리

int sum = strStream.parallel()	// 병렬 스트림으로 전환
				.mapToInt(s -> s.length()).sum();

parallel()로 병렬 처리해서 결과를 빨리 얻을 수 있다. 

 

6) 기본형 스트림 - IntStream, LongStream, DoubleStream 등

- 오토박싱 & 언박싱(기본형 => 객체. 객체 => 기본형. )의 비효율이 제거된다. 이렇게 사용하면 속도가 빨라진다

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

 

몰라도 되지만 스트림으로 작업하는 데 시간이 오래 걸리고, 좀 더 성능을 개선하고 싶으면 기본형 스트림을 공부해야 한다. 

 

4. 다양한 스트림

1) 컬렉션 

Stream<E> stream()

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

 

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

 

2) 배열

 

객체 배열로 스트림 생성

Stream<T> Stream.of(T... values) // 가변 인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<String> Stream.of("a","b","c");
Stream<String> Stream.of(new String[]{"a","b","c"});
Stream<String> Arrays.stream(new String[]{"a","b","c"});

 

기본형 배열로 스트림 생성

IntStream IntStream.of(int...values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])

숫자 스트림을 다룰 때는 IntStream 같은 기본형 스트림으로 다뤄야 빨라진다. sum이나 avg 같은 편리한 메소드도 쓸 수 있다. 

 

3) 파일

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

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

경로를 주면 폴더나 파일들로 이루어진 스트림을 생성할 수 있다. 지정된 파일들을 쉽게 다룰 수 있게되는 것

 

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

여기서 lines는 파일내용을 라인 단위로 읽어 String으로 만들어서 Stream<String>으로 만든다. 

 

 

 

출처: 자바의 정석

728x90