■ Stream API
- Java 8에 추가된 java.util.stream 패키지
- 컬렉션 요소를 람다식으로 처리할 수 있도록 돕는 함수형 프로그래밍 도구
• Stream API 특징
- 간결한 코드로 작성할 수 있다.
- 하지만 일반적으로 Stream으로 반복문을 실행하는 것보다 for문을 이용하는 방법이 빠르기 때문에, 무작정 사용하는 것은 속도를 느리게 만들 수 있다.
// JAVA 7
List<String> list = Arrays.asList("fast", "campus", "rocks");
List<String> newList = new ArrayList<>();
for (String s: list) {
newList.add(s.toUpperCase());
}
for (String s: newList) {
System.out.println(s);
}
// JAVA 8
List<String> list = Arrays.asList("fast", "campus", "rocks");
Stream<String> stream = list.stream();
stream.map(String::toUpperCase).forEach(System.out::println);
- 데이터 소스에 대한 공통된 접근 방식 제공, Stream으로 변경해주고 나면 List, Set, Map 모두 동일한 방식으로 데이터를 처리할 수 있다.
- 중간 처리와 최종 처리를 지원
• Stream 생성 방식
1. Collection의 인스턴스 메소드
- Stream stream() 메소드 이용
List<String> list1 = Arrays.asList("fast", "campus", "rocks");
Stream<String> stream1 = list1.stream();
2. Arrays를 이용하는 방법
- Arrays.stream() 메소드 이용
- IntStream, LongStream, DoubleStream 가능
int[] ints = {4, 6, 2, 19, 2, 58, 4, 6, 5};
IntStream intStream = Arrays.stream(ints);
3. Stream 클래스의 클래스 메소드
- Stream.of() 메소드 이용
DoubleStream doubleStream = DoubleStream.of(0.4, 0.6, 0.2, 1.2);
4. Stream의 range를 이용하는 방법
IntStream intStream1 = IntStream.range(0, 10); // 10은 포함되지 않는다.
intStream1.forEach(System.out::println);
IntStream intStream2 = IntStream.rangeClosed(0, 10); // 10이 포함된다.
// LongStream도 range, rangeClosed가 있다.
5. Random 객체를 이용한 방법
- ints(), longs(), doubles() 메소드
- ints(streamSize, randomNumberOrigin, randomNumberBound)
Random random = new Random();
LongStream longStream = random.longs(100); // 100개 제한
longStream.forEach(System.out::println);
LongStream longStream2 = random.longs(100, 0, 1000); // 0 부터 1000개
• Stream API 종류
1. Stream 종류
종류 | 처리 대상 |
Stream | 일반적인 객체를 처리 |
IntStream | 기본 자료형 int를 처리 |
LongStream | 기본 자료형 long를 처리 |
DoubleStream | 기본 자료형 double를 처리 |
2. 중간 처리 메소드
1) Filtering 메소드
- Stream distinct() - 스트림에 같은 요소가 있을 경우, 하나만 남기고 삭제하는 메소드
stringStream.distinct().forEach(System.out::println);
- Stream filter(Predicate<? super T> predicate) - Predicate 계열을 입력받아, true인 요소를 남긴다.
stringStream = Stream.of("Java", "Is", "Fun", "Isn't", "It", "?", "Java", "Java");
stringStream.filter(s -> s.length() >= 3)
.forEach(System.out::println);
2) Cutting 메소드
- Stream limit(long maxSize) : 스트림의 최대 요소 개수가 maxSize인 스트림 반환
- Stream skip(long n) : 스트림의 최초 n개를 생략하는 메소드
3) Sorting 메소드
- compareTo() 또는 입력받은 Comparator를 이용해 정렬
- Stream sorted() : Comparator 객체를 정렬한 스트림 반환
- Stream sorted(Comparator<? super T> comparator) : Comparator를 이용하여 정렬
stringStream = Stream.of("abc", "fwf", "dnmov", "work");
stringStream.sorted().forEach(System.out::println); // String 객체의 comateTo를 사용
stringStream = Stream.of("abc", "fwf", "dnmov", "work");
stringStream.sorted((o1, o2) -> o1.length() - o2.length()) // Comparator 인터페이스를 람다식으로 구현
.forEach(System.out::println);
System.out.println();
4) Mapping 메소드
- Function 계열의 인터페이스를 사용하여 스트림의 각 요소를 매핑
- Stream map(Function<? super T, ? extends R> mapper) : 기존 스트림의 T 타입 요소를 R 타입으로 변환하여 새로운 스트림 반환
- PStream mapToP(ToPFunction<? super T> mapper) : R이 기본형 타입으로 제한된 map()
stringStream = Stream.of("abc", "fwf", "dnmov", "work");
// Function 계열로 String -> Integer로 변환하는 매핑, Function<String, Integer>
Stream<Integer> stream2 = stringStream.map(s -> s.length());
stream2.forEach(System.out::println);
- Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) : 스트림의 T 타입 요소가 n개의 R 타입 요소로 매핑된 새로운 스트림을 반환
- PStream flatMapToP(Function<? super T, ? extends PStream> mapper) : R이 기본형 타입으로 제한된 flatMap()
List<String> list2 = Arrays.asList("java", "backend", "best", "course");
System.out.println(list2.stream().flatMap(data -> {
return Arrays.stream(data.split(""));
}).distinct().count());
5) Peek(조회) 메소드
- 중간 결과를 출력해 볼 수 있는 메소드(디버깅 기능)
- Stream peek(Consumer<? super T> action) : Consumer 계열의 람다식으로 입력받아 입력 요소를 소비
list2 = Arrays.asList("java", "backend", "best", "course");
list2.stream().flatMap(data -> {
return Arrays.stream(data.split(""));
}).peek(s -> {
System.out.println(s);
}).distinct().forEach(System.out::println);
3. 최종 처리 메소드
1) Maching 메소드
- boolean 타입의 값을 리턴
- boolean allMatch(Predicate<? super T> predicate) : 모든 요소가 true일 경우, true를 리턴
- boolean anyMatch(Predicate<? super T> predicate) : 하나라도 요소가 true일 경우 true를 리턴
- boolean noneMatch(Predicate<? super T> predicate) : 모든 요소가 false인 경우, true 리턴
Stream<String> st0 = Stream.of("avc", "def", "gop");
System.out.println(st0.allMatch(s -> s.equals("avc"))); // false
st0 = Stream.of("avc", "def", "gop");
System.out.println(st0.anyMatch(s -> s.equals("avc"))); // true
st0 = Stream.of("avc", "def", "gop");
System.out.println(st0.noneMatch(s -> s.equals("avc"))); // false
2) 집계(통계)
- 기본형 스트림(Int, Long, Double) : count(), sum(), average(), min(), max()
- Stream<T> 스트림 : count(), min(), max() -> (min과 max는 Comparator 구현 필요)
- T reduce(T identity, BinaryOperator accumulator) : identity를 초기값으로 하여, accumulator를 이용해 누적 집계 연산
* sum()이 실제로도 reduce로 구현되어 있다
IntStream.range(0, 10).reduce(0, (value1, value2) -> value1 + value2);
3) 반복 메소드
- void forEach(Comsumer<? super T> action) : Consumer 계열의 람다식을 입력받아, 각 요소를 소비
4) 수집 메소드
- Collectors 클래스의 정적 메소드를 이용하는 방법
- <R, A> R collect(Collector<? super T, A, R> collector) : Collection으로 변환시키는 메소드
- Collectors.toList() : ArrayList 반환
- Collectors.toSet() : HashSet 반환
- Collectors.toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) : Map<K, U> 반환
- Collectors.toCollection(Supplier collectionFactory) : 입력 값으로 Collector로 변환
// Collectors 클래스의 정적 메소드를 이용하는 방법
// toList() 메소드를 쓸 경우, ArrayList로 Collect하는 Collector 반환
String[] array = {"Collection", "Framework", "is", "so", "cool"};
Stream<String> stream3 = Arrays.stream(array);
List<String> collected = stream3.filter(s -> s.length() >= 3)
// .collect(Collectors.toList());
.collect(Collectors.toCollection(LinkedList::new));
System.out.println(collected);
// toSet() 메소드를 쓸 경우, HashSet으로 collect하는 Collector 반환
Stream<String> stream4 = Arrays.stream(array);
Set<String> collected2 = stream4.filter(s -> s.length() >= 3)
.collect(Collectors.toSet());
System.out.println(collected2);
// toMap() : Map<K, V> -> Map.Entry<K, V> - Key, Value를 정해줘야 된다.
Stream<String> stream5 = Arrays.stream(array);
Map<String, Integer> collected3 = stream5.filter(s -> s.length() >= 3)
.collect(Collectors.toMap(s->s, String::length));
System.out.println(collected3);
5) 그룹화/분리 메소드
- Map<R, List> groupingBy(Function<? super T, ? extends K> classifier) : Key 값으로 Function을 입력받고, Key를 기준으로 List 타입으로 그룹화한다.
- Map<Boolean, List> partitioningBy(Predicate<? super T> predicate) : Key 값으로 Predicate를 입력받고, true, false에 따라 List 타입으로 분리된다.
String[] arr = {"Python", "is", "aweful", "lame", "not", "good"};
Map<Integer, List<String>> map = Arrays.stream(arr)
.collect(Collectors.groupingBy(s -> s.length()));
System.out.println(map);
Map<Boolean, List<String>> map2 = Arrays.stream(arr)
.collect(Collectors.partitioningBy(s -> s.length() < 4));
- 그룹화 + Downstream collector
- Collector 중에도 counting(), summingP(), averagingP(), maxBy(), minBy(), reducing() 이 있다.
Map<Integer, Long> map3 = Arrays.stream(arr)
.collect(Collectors.groupingBy(String::length,Collectors.counting()));
6) 병렬 스트림
- 생성 방법
a. stream() 대신 parallelStream()으로 변경
b. stream 생성 후 parallel()으로 병렬화
Stream<String> parStream = Arrays.stream(arr).parallel();
System.out.println(parStream.map(String::length).count());
List<String> list4 = List.of("atew", "bff", "cqqwq", "dassa");
Stream<String> stream6 = list4.parallelStream(); // 병렬이기 때문에 연산 순서가 달라질 수도 있다! 내부적으로 여러 쓰레드가 계산하기 때문에
stream6.map(String::length)
.peek(s -> System.out.println("A:" + s))
.filter(value -> value > 3)
.peek(s -> System.out.println("B:" + s))
.forEach(System.out::println);
'Coding > Java' 카테고리의 다른 글
Java Code Convention(Google Java Style 정리) (0) | 2020.10.07 |
---|---|
java.lang 패키지 (0) | 2020.09.29 |
Java Comparator & Comparable (0) | 2020.09.22 |
Java Lambda Expression(람다식) (0) | 2020.09.18 |
Java Inner Class(내부 클래스) (0) | 2020.09.18 |