자바 스트림
Java 8부터 추가된 기술로 람다를 활용해 배열과 컬렉션을 함수형으로 간단하게 처리할 수 있는 기술로, 많은 수의 데이터를 다룰 때 유용함
기존 방식
- for문과 Iterator를 사용
- 코드가 길어져서 가독성과 재사용성이 떨어짐
- 데이터 타입(List, Set 등)마다 다른 방식으로 다뤄야 함
스트림
- 데이터 소스를 추상화함
- 데이터 소스에 상관없이 모두 같은 방식으로 다룰 수 있으므로 코드의 재사용성이 높아짐
- 데이터를 다루는 데 자주 사용되는 메소드를 정의해 놓음
특징
- 데이터 소스를 변경하지 않는다
- 한번 사용하면 닫혀서 다시 사용할 수 없는 일회용이다
- 반복문을 메서드의 내부에 숨길 수 있다
- 데이터 소스를 스트림으로 변환한 후 여러 번의 중간연산과 마지막의 최종 연산을 통해 다룰 수 있다
- 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다
- 데이터 소스의 요소를 기본형으로 다루는 스트림, IntStream, LongStream, DoubleStream이 제공된다
- 오토박싱&언박싱의 비효율을 줄인다
- 숫자의 경우 더 유용한 메서드를 Stream<T>보다 더 많이 제공한다
- 내부적으로 fork&join 프레임웍을 이용해서 자동적으로 연산을 병렬로 수행한다(멀티 쓰레드)
- 병렬 연산O - parallel()
- 병렬 연산X (기본값) - sequential()
스트림 사용
스트림 생성
- Collection의 자손인 List, Set을 구현한 컬렉션 클래스들은 stream()으로 스트림 생성
List<Integer> list = Arrays.asList(1,2,3,4,5); // 가변인자
Stream<Integer> intStream = list.stream(); // list를 소스로 하는 컬렉션 생성
intStream.forEach(System.out::println); // stream의 모든 요소 출력
- 객체 배열로부터 스트림 생성
// 첫번째 방법
Stream<String> strStream1 = Stream.of("a", "b", "c");
// 두번째 방법
Stream<String> strStream2 = Stream.of(new String[]{"a", "b", "c"});
// 세번째 방법
String[] strArr = new String[]{"a", "b", "c"};
Stream<String> strStream3 = Stream.of(strArr);
// 네번째 방법
Stream<String> strStream4 = Arrays.stream(strArr);
- 람다식 - iterate(), generate()
- 기본형 스트림 타입의 참조변수로 다루면 에러
// iterate(T seed, UnaryOperator f) 단항 연산자
Stream<Integer> intStream7 = Stream.iterate(0, n -> n + 2);
intStream7.limit(10).forEach(System.out::println);
// generate(Supplier s) : 주기만 하는 것. 입력X, 출력O
Stream<Integer> oneStream = Stream.generate(() -> 1);
oneStream.limit(10).forEach(System.out::println);
스트림의 중간연산
- 스트림 자르기 - skip(), limit()
IntStream intStream8 = IntStream.rangeClosed(1,10); // 1~10의 요소를 가진 스트림
intStream.skip(3).limit(5).forEach(System.out::print); // 45678
- 스트림의 요소 걸러내기 - filter(), distinct()
IntStream intStream9 = IntStream.of(1,2,2,3,3,3,4,5,5,6);
intStream9.distinct().forEach(System.out::print); // 123456
IntStream intStream10 = IntStream.rangeClosed(1, 10); // 1~10
intStream10.filter(i -> i%2 == 0).forEach(System.out::print); // 246810
- 정렬 - sorted()
- 지정된 Comparator로 스트림 정렬
Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
strStream.sorted().forEach(System.out::print); // CCaaabccdd
- 반환 - map()
- 스트림의 요소에 저장된 값 중에 원하는 필드만 뽑아내거나 특정 형태로 변환할 때 사용
List<String> list2 = Arrays.asList("hello", "java", "world");
list2.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
- 조회 - peek()
- 연산과 연산 사이에 올바르게 처리되었는지 확인
List<Integer> list3 = Arrays.asList(1, 2, 3, 4, 5);
OptionalDouble result = list3.stream()
.filter(num -> num % 2 == 0)
.peek(num -> System.out.println(num))
.mapToInt(num -> num)
.average();
System.out.println(result);
스트림의 최종연산
- 리듀싱 - reduce()
- 스트림의 요소를 줄여나가면서 연산을 수행하고 최종결과를 반환
- Identity : 계산을 수행하기 위한 초기값
- Accumulator : 각 요소를 계산한 중간 결과를 생성하기 위해 사용하는 누적작업
- Combiner : 병렬 스트림에서 나누어 계산된 결과를 하나로 합치기 위해 사용(싱글일 때는 사용하지 않음)
String[] strArr2 = {"Apple", "Banana", "tomato", "watermelon"};
int sum = Stream.of(strArr) // strArr 배열을 스트림으로 변환
.mapToInt(String::length) // 각 문자열의 길이를 추출. 스트림의 데이터 타입이 IntStream으로 변경
.reduce(0, (a, b) -> a + b); // IntStream의 모든 요소(문자열 길이)를 합계로 계산
System.out.println(sum); // Output: 27
IntStream intStream = IntStream.of(1,2,2,3,3,3,4,5,5,6);
OptionalInt min = intStream.reduce(Integer::min);
System.out.println(min.getAsInt()); // 1
- collect()
- 그룹별 리듀싱이 가능
- collect()는 Collector를 매개변수로 하는 스트림의 최종연산
- Collector는 수집(collect)에 필요한 메서드를 정의해 놓은 인터페이스
- Collectors클래스는 다양한 기능의 컬렉터(Collector를 구현한 클래스)를 제공
Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로
Collectors클래스
변환 mapping(), toList(), toSet(), toMap(), toCollection(), … 통계 counting(), summingInt(), averagingInt(), maxBy(), minBy(), summarizingInt() 문자열 결합 joining() 리듀싱 reducing() 그룹화와 분할 groupingBy(), partitioningBy(), collectingAndThen()
스트림을 컬렉션으로 변환
List<String> names = stuStream.map(Student::getName) // Stream<Student>→Stream<String>
.collect(Collectors.toList()); // Stream<String>→List<String>
ArrayList<String> list = names.stream()
.collect(Collectors.toCollection(ArrayList::new)); // Stream<String>→ArrayList<String>
Map<String,Person> map = personStream
.collect(Collectors.toMap(p->p.getRegId(), p->p));// Stream<Person>→Map<String,Person>
스트림의 통계
Collectors 클래스는 그룹별 연산가능
long count = stuStream.count(); // 전체 카운팅
long count = stuStream.collect(counting()); // Collectors.counting() 그룹별 연산 가능
long totalScore = stuStream.mapToInt(Student::getTotalScore).sum(); // IntStream의 sum()
long totalScore = stuStream.collect(summingInt(Student::getTotalScore)); // 그룹별 연산 가능
OptionalInt topScore = studentStream.mapToInt(Student::getTotalScore).max();
Optional<Student> topStudent = stuStream
.max(Comparator.comparingInt(Student::getTotalScore));
Optional<Student> topStudent = stuStream
.collect(maxBy(Comparator.comparingInt(Student::getTotalScore))); // 그룹별 연산 가능
스트림을 리듀싱 - reducing()
reduce()는 전체 리듀싱
Collectors.reducing()은 그룹별 리듀싱 가능
IntStream intStream = new Random().ints(1,46).distinct().limit(6);
OptionalInt max = intStream.reduce(Integer::max); // 전체 리듀싱
Optional<Integer> max = intStream.boxed().collect(reducing(Integer::max)); // 그룹별 리듀싱 가능
long sum = intStream.reduce(0, (a,b) -> a + b); // 전체 리듀싱
long sum = intStream.boxed().collect(reducing(0, (a,b)-> a + b)); // 그룹별 리듀싱 가능
int grandTotal = stuStream.map(Student::getTotalScore).reduce(0, Integer::sum); // 전체 리듀싱
int grandTotal = stuStream.collect(reducing(0, Student::getTotalScore, Integer::sum)); // 그룹별 리듀싱 가능
'Java > Java' 카테고리의 다른 글
[Java(자바)] 오버라이딩과 오버로딩 (0) | 2024.05.22 |
---|---|
[Java(자바)] JVM(Java Virtual Machine) (0) | 2024.05.12 |
[Java(자바)] Java 21 - Virtual thread (0) | 2024.05.12 |
[Java(자바)] 자바의 Thread(쓰레드) (0) | 2024.05.12 |
[Java(자바)] 메모리 관리와 가비지 컬렉션 (0) | 2024.05.12 |