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단계이다.
- 처음에 List, Set, Map 등과 같은 컬렉션을 받는다.
- N번의 중간 연산을 한다.
- 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>으로 만든다.
출처: 자바의 정석
'JAVA > JAVA' 카테고리의 다른 글
스트림(Stream) - 최종 연산(3/3) (0) | 2023.01.05 |
---|---|
스트림(Stream) - 중간 연산(2/3) (0) | 2023.01.04 |
Generics 지네릭스 - 와일드 카드 및 메소드 (2/2) (0) | 2023.01.02 |
Generics 지네릭스 - 개념 및 활용 (1/2) (0) | 2023.01.02 |
int, long 타입과 같은 Primitive type null 값이 안 들어가는 이유 (0) | 2021.07.04 |