이 글은 유튜브 '자바의 정석 - 기초편'을 보고 정리한 글입니다.
📂content
1. 스트림(Stream)
- 다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것
- 데이터 소스 = 컬렉션, 배열과 같이 여러 데이터를 저장한 것
표준화된 방법 = 컬렉션 프레임워크도 표준화된 방법으로 다루기 위해 정리한 것이긴 한데, 사실 실패함.
List&Set / Map으로 나누어졌기때문이다. 그런데 jdk1.8부터 stream이 등장하면서 표준화하는 것을 성공했다.
- 다양한 데이터소스(컬렉션, 배열 등)으로부터 stream을 만들 수 있다. 그리고 이 후 같은 방식으로 작업을 처리할 수 있다. 중간연산, 최종연산이 있는데 중간연산은 n번, 최종연산은 1번만 할 수 있다.
- stream을 이용한 작업
1. 스트림 만들기 -> 2. 중간연산(0~n번) -> 3. 최종연산(1번)
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)l; //난수 스트림(크기가 5)
//Stream<T> Collection stream()
//Collection에는 stream이라는 메소드가 있는데 반환값으로 Stream<T>를 반환한다.
- 스트림이 제공하는 기능 - 중간 연산과 최종 연산
- 중간 연산
연산결과가 스트림인 연산. 반복적으로 적용 가능
- 최종 연산
연산결과가 스트림이 아닌 연산. 단 한번만 적용 가능(스트림 요소를 소모)
String[] strArr = { "dd","aaa","CC","cc","b" };
Stream<String> stream = Stream.of(strArr); // 1.문자열 배열이 소스인 스트림
Stream<String> filteredStream = stream.filter(); // 2.걸러내기(중간 연산)
Stream<String> distinctedStream = stream.distinct(); // 2.중복제거(중간 연산)
Stream<String> sortedStream = stream.sort(); // 2.정렬(중간 연산)
Stream<String> limitedStream = stream.limit(5); // 2.스트림 자르기(중간 연산)
int total = stream.count(); // 3.요소 개수 세기(최종연산)
최종연산 forEach의 결과는 void이다.
2. 스트림(Stream)의 특징
- 스트림은 데이터 소스(원본)로부터 데이터를 읽기만할 뿐 변경하지 않는다.
- 스트림은 Iterator처럼 일회용이다. (필요하면 다시 스트림을 생성해야 함)
- 최종 연산 전까지 중간연산이 수행되지 않는다. - 지연된 연산
스트림에는 유한/무한 스트림이있다. 위 예시는 무한 스트림이다. 이 스트림은 무한 스트림이라 난수를 끝도 없이 준다. 그런데 이 스트림에 중복제거, 자르기, 정렬인 중간연산을 하고 최종연산을 한다.
무한스트림에서 중복제거가 가능할까? 안 된다고 생각할 수 있지만 된다!
중간연산은 필요할 때 나중에 수행이 되기 때문이다. 즉, 지연된 연산이 이루어져서 가능하다.
- 스트림은 작업을 내부 반복으로 처리한다.
for문을 forEach라는 메서드 안에 넣어버렸다. 성능은 비효율적이지만 코드가 간결해진다는 장점이 있다.
- 스트림의 작업을 병렬로 처리 - 병렬스트림
parallelStream()의 반대는 Sequential()이다. Sequential()이 default값이다.
- 기본형 스트림 - IntStream, LongStream, DoubleStream
오토박싱 & 언박싱의 비효율이 제거됨(Stream <Integer> 대신 intStream사용)
숫자와 관련된 유용한 메서드를 Stream<T>보다 더 많이 제공
- Stream<Integer>은 Stream의 요소가 Integer이라는 이야기이다.
Stream<T>인데 T에는 기본형이 올 수 없고, 참조형만 가능하다.
그래서 int[]을 Stream을 바꾸면 1이 new Integer(1)로 바뀐다. 즉, 기본형에서 참조형으로 바뀐다.
즉, 변환(오토박싱)이 필요하다. 그런데 여기서 시간이 소모가 되고, 너무 많은 데이터가 있다면 이게 비효율적이다. 그래서 Stream<Integer>대신 intStream을 사용한다.
데이터가 기본형일 때 기본형 스트림을 쓰면 성능이 좋아질 것 이다.
- Stream<T>에 T가 숫자외에도 여러 타입의 스트림이 가능해야하므로 숫자 스트림에만 사용할 수 있는 sum(), average()를 넣지 않는 것이다. 기본형 스트림은 다 숫자라는 것을 알아서 sum(), count(), average() 등을 제공한다.
- 사실 이 부분은 몰라도 되는데 스트림을 사용할 때 오토박싱&언박싱 부분에서 성능이 떨어진다면 그 때 사용해보자!
3-1. 스트림 만들기 - 컬렉션
- Collection인터페이스의 stream()으로 컬렉션을 스트림으로 변환
Stream<E> stream() //Collection인터페이스의 메서드
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream();// list를 스트림으로 변환
// 스트림의 모든 요소를 출력
intStream.forEach(System.out::print); //12345 //메서드 참조
intStream.forEach(System.out::print); //에러. 스트림이 이미 닫혔다.
forEach는 최종연산이다. stream은 1회용이고, stream에 대해 최종연산을 수행하면 stream이 닫힌다.
3-2. 스트림 만들기 - 배열
- 객체 배열로부터 스트림 생성하기
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으로 만듦
//from~to인데 to는 포함X
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);
- 기본형 배열로부터 스트림 생성하기
IntStream IntStream.of(int... values) //Stream이 아니라 IntStream
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)
3-3. 스트림 만들기 - 임의의 수
- 난수를 요소로 갖는 스트림 생성하기
- 지정된 범위의 난수를 요소로 갖는 스트림을 생성하는 메서드(Random클래스)
3-4. 스트림 만들기 - 특정 범위의 정수
- 특정 범위의 정수를 요소로 갖는 스트림 생성하기 (IntStream, LongStream)
IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end) //끝이 포함됨.
3-5. 스트림 만들기 - 람다식 iterate(), generate()
- 람다식을 소스로 하는 스트림 생성하기
- iterate()는 이전 요소를 seed로 해서 다음 요소를 계산한다.
Stream<Integer> evenStream = Stream.iterate(0, n -> n+2); //0, 2, 4, 6
- generate()는 seed(초기값)를 사용하지 않는다.
Stream<Double> randomStream = Stream.generate(Math::random);
Stream<Integer> oneStream = Stream.generate(() -> 1);
generate()는 각 요소가 독립적이라서 이전 결과와 상관이 없다. iterate()는 이전 결과를 사용한다.
3-6. 스트림 만들기 - 파일과 빈 스트림
- 파일을 소스로 하는 스트림 생성하기
lines() : 파일 내용을 라인 단위로 읽어서 String으로 만들어서 stream<String>으로 만든다.
- 비어있는 스트림 생성하기
Stream emptyStream = Stream.empty(); //empty()는 빈스트림을 생성해서 반환한다.
long count = emptyStream.count(); //count의 값은 0
출처
'🎥Back > 자바의 정석' 카테고리의 다른 글
[JAVA의 정석]Optional (0) | 2024.03.09 |
---|---|
[JAVA의 정석]스트림의 연산 (0) | 2024.03.08 |
[JAVA의 정석]람다식과 함수형 인터페이스 (2) | 2024.03.06 |
[JAVA의 정석] 스레드와 관련된 함수 및 동기화 (0) | 2024.03.01 |
[JAVA의 정석]데몬 스레드, 스레드의 상태 (0) | 2024.02.29 |