스트림(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 같은 숫자형 메서드는 없다.
스트림 만들기 - 임의의 수
- 난수를 요소로 갖는 스트림 생성하기
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
'Java' 카테고리의 다른 글
Java : 스트림의 연산,중간 연산(2) - 기초 (0) | 2021.09.04 |
---|---|
Java : 스트림의 연산,중간 연산(1) - 기초 (0) | 2021.09.03 |
Java : GC(Garbage collector)에 대해서 - 기초 (0) | 2021.08.25 |
Java : JVM 이란? 자바 메모리 구조 (0) | 2021.08.25 |
Java : 람다식 java.util.function 패키지 - 기초 (0) | 2021.08.16 |