기본형(Primitive Type) Stream
1) boxed().collect(..)
- Primitive Type 의 Stream을 Boxing Type 의 Stream<T>으로 변환한다
- boxed()의 경우 중간 연산에 속하나 collect() method chaning 방식에 대해 설명하기 위해 추가
Stream<Integer> integerStream = IntStream.of(1, 2, 3, 4, 5).boxed();
Stream<Long> longStream = LongStream.of(1, 2, 3, 4, 5).boxed();
Stream<Double> doubleStream = DoubleStream.of(1.1, 2.2, 3.3, 4.4, 5.5).boxed();
// List 로 최종연산 처리
List<Integer> integers = integerStream.collect(Collectors.toList());
List<Long> longs = longStream.collect(Collectors.toList());
List<Double> doubles = doubleStream.collect(Collectors.toList());
2) toArray()
stream data source를 배열[]로 변환
int[] numbers = IntStream.range(1, 5).toArray();
long[] longs = LongStream.rangeClosed(10L, 15L).toArray();
String[] strNumber = new Random().ints().limit(10).mapToObj(String::valueOf).toArray(String[]::new);
assertThat(strNumber).isInstanceOf(String[].class); // Ok
- Primitive Type Stream 의 경우 toArray() 시 각 타입에 맞는 배열을 리턴
IntStream.toArray() 의 경우 int[] 리턴
- Stream.class 의 toArray()는 Object[] 리턴하기 때문에 형 변환이 필요한 경우 위와 같이 인자에 지정해서 사용
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> var1);
3) sum()
elements 합산한 결과를 리턴한다
// IntStream.class
int sum();
// 사용 예시
int sumValue = IntStream.range(1, 100).sum();
assertThat(sumValue).isEqualTo(4950); // 100 * 99 / 2 = 4950
4) count()
- elements 수를 카운트한 결과를 리턴한다
- 결과값 타입이 long
// IntStream.class
long count();
// 사용예시
long count = IntStream.rangeClosed(1, 100).count(); // 1 ~ 100
assertThat(count).isEqualTo(100L);
5) max()
elements 중 최대값을 찾아 OptionalInt 객체로 반환
int maxValue = IntStream.of(10, 18, 20, 70, 4).max().getAsInt();
assertThat(maxValue).isEqualTo(70);
6) min()
elements 중 최소값을 찾아 OptionalInt 객체로 반환
int minValue = IntStream.of(99, 23, 328, 9, 1).min().getAsInt();
assertThat(minValue).isEqualTo(1);
7) average()
elements 의 평균값을 구해 OptionalDouble 객체로 반환
double avg = IntStream.of(1, 2, 3, 4, 5).average().getAsDouble();
assertThat(avg).isEqualTo(3.0);
IntSummaryStatistics 와 같은 통계 클래스 제공
Collection Stream
1) forEach(..), forEachOrdered(..)
- 각 element에 지정된 작업 수행
- 중간 연산 peek의 경우 elements 를 조회하는 용도였다면, forEach는 elements를 소비하여 확인하는 용도
- 병렬 연산시 forEachOrdered()는 Thread Safe 하다
- 병렬 연산시 forEach는 Thread Safe 하지 않다 (반대로 직렬 연산시 forEachOrdered보다 성능이 좀 더 나음)
예시
List<Integer> list = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
list.stream().parallel().forEach(System.out::println); // 8 5 6 4 9 2 1 10 7 3
list.stream().parallel().forEachOrdered(System.out::println); // 1 2 3 4 5 6 7 8 9 10
2) count()
스트림 elements의 개수 반환
예시
List<Integer> list = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
long count = list.stream().count();
assertThat(count).isEqualTo(10); // ok
- stream 중간 연산이 따로 없기 때문에 int size() 호출하여 elements 수를 구하는게 더 나을 것이다
3) max(..), min(..)
:스트림의 elements 중 최대값/최소값을 반환
Stream<T> 클래스의 경우 Comparator 파라미터 받음 (Primitive Type Stream은 파라미터 x)
max() : 최대값 찾기
//Stream.class
Optional<T> max(Comparator<? super T> comparator);
//IntStream.class
OptionalInt max();
예시
// Stream.class method 사용
List<Integer> list = IntStream.rangeClosed(1, 100).boxed().collect(Collectors.toList());
int max = list.stream().max(Comparator.comparing(v -> v)).orElse(-1); // 100
// Integer(Warrper) -> IntStream (Primitive) 변환 후 method 사용
int max2 = list.stream().mapToInt(v -> v).max().orElse(-1); // 100
min() : 최소값 찾기
// Stream.class
Optional<T> min(Comparator<? super T> comparator);
// IntStream.class
OptionalInt min();
예시
//Stream.class method 사용
List<Integer> list = IntStream.rangeClosed(1, 100).boxed().collect(Collectors.toList());
int min = list.stream().min(Comparator.comparing(v -> v)).orElse(-1);
// Integer(Wrapper) -> IntStream (Primitive) 변환 후 method 사용
int min2 = list.stream().mapToInt(v -> v).min().orElse(-1);
4) findFirst(), findAny()
- 조건에 맞는 element 찾기
- findAny() 스트림의 element 중 아무거나 한 가지 반환
- findFirst() 스트림의 첫번째 element 반환
Optional<T> findFirst()
직렬/병렬 모두 동일한 결과값 리턴
// 직렬 (sequential)
List<String> list = Arrays.asList("A", "B", "C", "D");
Optional<String> result = list.stream().findFirst();
assertThat(result.get()).isEqualTo("A"); // Ok
// 병렬 (parallel)
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = list.stream().parallel().findFirst();
assertThat(result.get()).isEqualTo(1); // Ok
Optional<T> findAny()
직렬은 동일하나 병렬 스트림의 경우 결과값이 항상 달라진다
// 직렬 (Sequential)
List<String> list = Arrays.asList("A", "B", "C", "D");
Optional<String> result = list.stream().findAny();
assertThat(result.get()).isEqualTo("A");
// 병렬 (parallel)
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = list.stream().parallel()
.filter(n -> n < 4).findAny();
assertThat(result.get()).isIn(1, 2, 3); // ok
assertThat(result.get()).isEqualTo(1); // fail, expected 1, but was 3
참고. findFirst() 와 findAny() 차이
- 직렬 스트림에서 사용시 둘은 동일한 요소를 리턴하여 동작의 차이가 없음
- 병렬 스트림에서 findFirst()는 첫번째 요소를 보장하지만, findAny() 첫번째 요소라는 것을 보장하지 않는다
5) allMatch(..), anyMatch(..), noneMatch(..)
: 조건에 따른 boolean 형 결과값 리턴
allMatch : 모든 요소가 조건을 만족하는 경우
// Stream.class
boolean allMatch(Predicate<? super T> predicate);
// 예시
List<Integer> integers = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
boolean result = integers.stream().allMatch(v -> v <= 10); // 모두 10이하인가
boolean result2 = integers.stream().allMatch(v -> v % 2 == 0); // 모두 짝수인가
assertThat(result).isTrue();
assertThat(result2).isFalse();
anyMatch : 하나라도 조건을 만족하는 요소가 있는지
// Stream.class
boolean anyMatch(Predicate<? super T> predicate);
// 예시
int[] ints = IntStream.of(1, 3, 5, 7, 10).toArray();
boolean result = Arrays.stream(ints).anyMatch(v -> v % 2 == 0); // 하나 이상 짝수인가
assertThat(result).isTrue();
noneMatch : 모든 요소가 조건을 만족하지 않는 경우
// Stream.class
boolean noneMatch(Predicate<? super T> predicate);
// 예시
int[] ints = IntStream.of(2, 4, 6, 8, 10).toArray();
boolean result = Arrays.stream(ints).noneMatch(v -> v % 2 == 1); // 모두 홀수인가
assertThat(result).isTrue();
6) toArray()
: 스트림의 모든 요소를 담은 배열로 변환
Stream.class
Object[] toArray()
A[] toArray(IntFunction<A[]> generator)
예시
// Primitive Type -> Object Type
String[] strNumber = new Random().ints().limit(10).mapToObj(String::valueOf).toArray(String[]::new);
assertThat(strNumber).isInstanceOf(String[].class); // Ok
int형 난수 생성 > int type을 String type으로 mapping > String 타입 배열([]) 처리
7) reduce(..)
스트림의 요소를 하나씩 줄여가면서 연산 결과를 구한다
#Stream.class
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)U reduce(T identity, BiFunction<U, T, U> accumulator, BinaryOperator<U> combiner>(생략)
- identity : 초기값
- BinaryOperator<T> accumulator : T 타입 입력 두 개 받아, T 타입 리턴하는 함수형 인터페이스
- BiFunction<U, T, U> accumulator : U 타입과 T타입 입력받아, U 타입 리턴하는 함수형 인터페이스
#IntStream.class
int reduce(int var1, IntBinaryOperator var2);
OptionalInt reduce(IntBinaryOperator var1);
BinaryOperator
- Collection Stream 사용시 reduce() 파라미터로 사용
- T 타입 입력 두개를 받아 , T 타입 결과 리턴
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T, T, T> {
//.. maxBy, minBy
}
IntBinaryOperator
- IntStream 사용시 reduce() 파라미터로 사용
- int 타입 입력 두 개 받아, int 타입 결과 리턴
@FunctionalInterface
public interface IntBinaryOperator {
int applyAsInt(int var1, int var2);
}
예시
IntStream.reduce(..) 로 작성
int[] numbers = IntStream.rangeClosed(1, 10).toArray();
int count = Arrays.stream(numbers).reduce(0, (a, b) -> a + 1); // 10
int sum = Arrays.stream(numbers).reduce(0, (a, b) -> a + b); // 55
int max = Arrays.stream(numbers).reduce(Integer.MIN_VALUE, Math::max); // (a, b) -> a < b ? a : b
int min = Arrays.stream(numbers).reduce(Integer.MAX_VALUE, Math::min);
assertThat(count).isEqualTo(10);
assertThat(sum).isEqualTo(55);
assertThat(max).isEqualTo(10);
assertThat(min).isEqualTo(1);
8) collect()
스트림의 요소를 수집/그룹화/분할한 결과를 컬렉션에 담아 반환한다
collect() : 최종 연산 메서드
Collector : 인터페이스
Collectors : Collector 인터페이스 구현체 (대부분 구현체에 정의 되어 있는 메서드 활용)
Stream.collect()
<R, A> R collect(Collector<? super T, A, R> var1);
interface Collector
// T 타입 요소를 A에 누적한다음, 결과를 R 타입으로 변환하여 리턴
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
//..
}
Collectors 클래스 정적 메서드 살펴보기
통계 메서드로 단일 값을 구할 경우 대체할 메서드가 존재하여 의미 없어 보였는데, 그룹화 대상으로 연산 할 경우 의미를 가지는 걸 확인
[변환]
8-1-1) toList()
ArrayList로 생성
//Collectors.class
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl(ArrayList::new, List::add, (left, right) -> {
left.addAll(right);
return left;
}, CH_ID);
}
8-1-2) toSet()
HashSet으로 생성
public static <T> Collector<T, ?, Set<T>> toSet() {
return new CollectorImpl(HashSet::new, Set::add, (left, right) -> {
if (left.size() < right.size()) {
right.addAll(left);
return right;
} else {
left.addAll(right);
return left;
}
}, CH_UNORDERED_ID);
}
8-1-3) toMap(..)
HashMap으로 생성
public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {
return new CollectorImpl(HashMap::new, uniqKeysMapAccumulator(keyMapper, valueMapper), uniqKeysMapMerger(), CH_ID);
}
8-1-4) toCollection()
- collectonFactory : 사용자가 원하는 콜렉션 생성자 전달
- Collector<T, ?, C> 에서 T 는 입력 타입, C는 반환하게 될 결과 타입
- C extends Collection<T>에서 C는 T타입을 담은 Collection 뜻함
- 이때 Map의 경우 Collection을 상속하지 않기 때문에 인자로 사용 x
public static <T, C extends Collection<T>> Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
return new CollectorImpl(collectionFactory, Collection::add, (r1, r2) -> {
r1.addAll(r2);
return r1;
}, CH_ID);
}
예시
int[] number = IntStream.rangeClosed(1, 10).toArray();
List<Integer> result1 = Arrays.stream(number).boxed().collect(Collectors.toList());
Set<Integer> result2 = Arrays.stream(number).boxed().collect(Collectors.toSet());
Map<String, Integer> result3 = Arrays.stream(number).boxed().collect(Collectors.toMap(String::valueOf, v -> v));
List<Integer> result4 = Arrays.stream(number).boxed().collect(Collectors.toCollection(LinkedList::new));
Set<Integer> result5 = Arrays.stream(number).boxed().collect(Collectors.toCollection(TreeSet::new));
[통계]
8-2-1) counting()
- Stream<T> 나 IntStream과 같은 기본형 스트림에서 count() 대체 사용 가능
long count = IntStream.of(1, 2, 3).boxed().collect(counting()); // count() 대체 가능
assertThat(count).isEqualTo(3);
8-2-2) summingInt()
int sum = IntStream.rangeClosed(1, 10).boxed().collect(summingInt(v -> v)); // mapToInt().sum() 대체 가능
assertThat(sum).isEqualTo(55);
8-2-3) averagingInt()
double avg = IntStream.rangeClosed(1, 3).boxed().collect(averagingInt(v -> v - 1)); // (0 + 1 + 2) / 3
assertThat(avg).isEqualTo(1);
8-2-4) maxBy(), minBy()
해당 값이 최대/최소인 객체 리턴
List<Box> boxes = List.of(new Box(8, "green"),
new Box(3, "blue"),
new Box(10, "yellow"));
// weight가 가장 큰
Box maxResult = boxes.stream().collect(Collectors.maxBy(Comparator.comparingInt(Box::getWeight))).orElse(null);
// colorName 길이가 가장 작은
Box minResult = boxes.stream().collect(Collectors.minBy(Comparator.comparingInt(o -> o.getColorName().length()))).orElse(null);
assertThat(maxResult).isEqualTo(new Box(10, "yellow"));
assertThat(minResult).isEqualTo(new Box(3, "blue"));
[문자열 결합]
8-3) joining()
public static Collector<CharSequence, ?, String> joining() { .. }
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) { .. }
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) { .. }
- joining()의 경우 StringBuilder를 내부적으로 생성해서 append하여 전달
- joining(CharSequence delimiter)의 경우 내부적으로 joining(delimiter, "", "") 호출
- joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) : 문자 사이에 delimiter 넣고 양옆에 prefix, suffix 붙여서 최종 문자열을 만듦
예시
String[] strings = new String[] {"a", "b", "c", "d", "e"};
String result = Arrays.stream(strings).collect(Collectors.joining()); // "abcde"
String result2 = Arrays.stream(strings).collect(Collectors.joining("|")); // "a|b|c|d|e"
String result3 = Arrays.stream(strings).collect(Collectors.joining(", ", "[", "]")); // "[a, b, c, d, e]"
assertThat(result).isEqualTo("abcde");
assertThat(result2).isEqualTo("a|b|c|d|e");
assertThat(result3).isEqualTo("[a, b, c, d, e]");
[누적연산]
8-4) reducing()
Collectors.class
public static <T> Collector<T,?,Optional<T>> reducing(BinaryOperator<T> op)
public static <T> Collector<T,?,T> reducing(T identity, BinaryOperator<T> op)
public static <T,U> Collector<T,?,U> reducing(U identity,
Function<? super T,? extends U> mapper,
BinaryOperator<U> op)
- identity : 초기값
- BinaryOperator<T> : T타입 입력 2개 받아, T 타입 출력
- mapper : T 타입 인자를 받아 U 타입으로 mapping 처리한 결과값 리턴
임의 Student 클래스 정의
class Student {
private String name;
private String city;
private double avgGrade;
private int age;
// 생성자, getter, equals, toString .. 생략
}
데이터 생성
private List<Student> getStudent() {
return List.of(
new Student("John Smith", "Miami", 7.38, 19),
new Student("Mike Miles", "New York", 8.4, 21),
new Student("Michael Peterson", "New York", 7.5, 20),
new Student("James Robertson", "Miami", 9.1, 20),
new Student("Joe Murray", "New York", 7.9, 19),
new Student("Kyle Miller", "Miami", 9.83, 20)
);
}
예시1. city 그룹화 후 최대 avgGrade 가진 Student 구하기
public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) { .. }
- BinaryOperator만 정의하는 경우 T 타입 입력을 받아, Optional<T> 타입 출력을 반환함
Map<String, Optional<Student>> cityAndMaxAvgGrade = students.stream().collect(
groupingBy(Student::getCity, Collectors.reducing(BinaryOperator.maxBy(Comparator.comparing(Student::getAvgGrade))))
);
assertThat(cityAndMaxAvgGrade)
.containsEntry("New York", Optional.of(new Student("Mike Miles", "New York", 8.4, 21)))
.containsEntry("Miami", Optional.of(new Student("Kyle Miller", "Miami", 9.83, 20))); // Ok
예시2. reducing()에 identity 정의하여 '예시1' 결과 구하기
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) { .. }
- identity(초기값) 정의할 경우 T 타입 입력을 받아, T 타입을 출력함 (Optional이 벗겨짐)
Student identity = new Student("", "", 0, 0);
Map<String, Student> cityAndMaxAvgGrade2 = students.stream().collect(
groupingBy(Student::getCity, Collectors.reducing(identity, BinaryOperator.maxBy(Comparator.comparing(Student::getAvgGrade))))
);
assertThat(cityAndMaxAvgGrade2)
.containsEntry("New York", new Student("Mike Miles", "New York", 8.4, 21))
.containsEntry("Miami", new Student("Kyle Miller", "Miami", 9.83, 20)); // ok
예시3. reducing()에 identity와 mapper 정의하여 double avgGrade 최대값 구하기
public static <T, U> Collector<T, ?, U> reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op) { .. }
- mapper가 추가되어 T타입 입력을 U타입으로 mapping 후, U 타입 결과를 리턴
// (input) Student -> (mapper) Student to double -> (output) double
double largestAvgGrade = students.stream().collect(Collectors.reducing(0.0, Student::getAvgGrade, BinaryOperator.maxBy(Comparator.comparingDouble(v -> v))));
assertThat(largestAvgGrade).isEqualTo(9.83); // ok
예시4. citry 그룹화 후 그룹별 최대 avgGrade 구하기
Map<String, Double> cityAndLargestAvgGrade = students.stream().collect(
groupingBy(Student::getCity, Collectors.reducing(0.0, Student::getAvgGrade, BinaryOperator.maxBy(Comparator.comparingDouble(v -> v))))
);
assertThat(cityAndLargestAvgGrade)
.containsEntry("New York", 8.4)
.containsEntry("Miami", 9.83); // ok
Collectors.reducing(...) 의 파라미터 인자에 따라 결과 타입이 달라지는 것을 확인 가능했다
[그룹화]
8-5-1) groupingBy()
- 스트림을 n 분할로 나눠 그룹화
- Collectors.mapping(), Collectors.reducing(), Collectors.counting() 등과 함께 그룹별 결과 처리 가능
Collectors.groupingBy()
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream);
}
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream) { .. }
- 파라미터에 따라 메서드가 분할 되어 있는 형태를 보임
임의 데이터
class Student {
private String name;
private String city;
private double avgGrade;
private int age;
// 생성자, getter, equals, toString .. 생략
}
private List<Student> getStudent() {
return List.of(
new Student("John Smith", "Miami", 7.38, 19),
new Student("Mike Miles", "New York", 8.4, 21),
new Student("Michael Peterson", "New York", 7.5, 20),
new Student("James Robertson", "Miami", 9.1, 20),
new Student("Joe Murray", "New York", 7.9, 19),
new Student("Kyle Miller", "Miami", 9.83, 20)
);
}
예시1. City 그룹별, Student 리스트
Map<String, List<Student>> groupingByCityName = students.stream()
.collect(Collectors.groupingBy(Student::getCity));
assertThat(groupingByCityName.get("New York").size()).isEqualTo(3);
assertThat(groupingByCityName.get("Miami").size()).isEqualTo(3);
예시2. City 그룹별, Student::getName
Map<String, List<String>> cityAndNameList = students.stream()
.collect(Collectors.groupingBy(Student::getCity, Collectors.mapping(Student::getName, toList())));
assertThat(cityAndNameList.get("New York")).containsExactly("Mike Miles", "Michael Peterson", "Joe Murray");
assertThat(cityAndNameList.get("Miami")).containsExactly("John Smith", "James Robertson", "Kyle Miller");
예시3. age 그룹별, counting
Map<Integer, Long> ageAndCountMap = students.stream()
.collect(Collectors.groupingBy(Student::getAge, Collectors.counting()));
assertThat(ageAndCountMap.get(19)).isEqualTo(2);
assertThat(ageAndCountMap.get(20)).isEqualTo(3);
assertThat(ageAndCountMap.get(21)).isEqualTo(1);
예시4. TreeMap::new 생성자 변경(기본 HashMap::new)
Map<String, List<String>> namesByCity = students.stream()
.collect(Collectors.groupingBy(
Student::getCity,
TreeMap::new,
Collectors.mapping(Student::getName, toList()))
);
assertThat(namesByCity).isInstanceOf(TreeMap.class);
8-5-2) partitioningBy()
스트림을 2분할로 나눠 그룹화
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
- T 타입 입력 받아 Map<Boolean, List<T>> 타입 출력
예시1. 문자열 길이가 4 초과 여부로 2분할
List<String> names = Arrays.asList("John", "Jane", "Michael", "Anna", "James");
Map<Boolean, List<String>> partitioningByNameLength = names.stream()
.collect(Collectors.partitioningBy(v -> v.length() > 4));
assertThat(partitioningByNameLength.get(true).size()).isEqualTo(2); // [Michael, James]
assertThat(partitioningByNameLength.get(false).size()).isEqualTo(3); // [John, Jane, Anna]
예시2. 문자열 길이가 4 초과 여부로 2분할 후, downstream 지정하여 처리
List<String> names = Arrays.asList("John", "Jane", "Michael", "Anna", "James");
Map<Boolean, List<String>> partitioningByNameLengthToUpperCase = names.stream()
.collect(Collectors.partitioningBy(s -> s.length() > 4, Collectors.mapping(String::toUpperCase, toList())));
assertThat(partitioningByNameLengthToUpperCase.get(true)).containsExactly("MICHAEL", "JAMES");
assertThat(partitioningByNameLengthToUpperCase.get(false)).containsExactly("JOHN", "JANE", "ANNA");
예시3. 조건 (student.getName().length() > 8 && student.getAvgGrade() > 8.0) 2분할
private List<Student> getStudent() {
return List.of(
new Student("John Smith", "Miami", 7.38, 19),
new Student("Mike Miles", "New York", 8.4, 21),
new Student("Michael Peterson", "New York", 7.5, 20),
new Student("James Robertson", "Miami", 9.1, 20),
new Student("Joe Murray", "New York", 7.9, 19),
new Student("Kyle Miller", "Miami", 9.83, 20)
);
}
List<Student> students = getStudent();
Map<Boolean, List<Student>> nameAndAvgGrade = students.stream()
.collect(Collectors.partitioningBy(student -> student.getName().length() > 8 && student.getAvgGrade() > 8.0));
assertThat(nameAndAvgGrade.get(true).size()).isEqualTo(3); // ok
예시4. 조건( avgGrade > 8.0 )으로 2분할 후 count 처리
Map<Boolean, Long> avgGradeCounting = students.stream()
.collect(Collectors.partitioningBy(s -> s.getAvgGrade() > 8.0, Collectors.counting()));
assertThat(avgGradeCounting.get(true)).isEqualTo(3L);
assertThat(avgGradeCounting.get(false)).isEqualTo(3L);
참고
https://stackabuse.com/java-8-streams-definitive-guide-to-partitioningby/
https://stackabuse.com/guide-to-java-8-collectors-groupingby/
https://stackabuse.com/guide-to-java-8-collectors-reducing/
https://www.baeldung.com/java-stream-findfirst-vs-findany
https://codechacha.com/ko/java8-stream-difference-findany-findfirst/
'공부 > Java' 카테고리의 다른 글
[Java] Reflection API 테스트 학습(with Baeldung) (0) | 2023.11.20 |
---|---|
[Java] Stream Quiz 개인 풀이 (출처. 망나니 개발자 기술 블로그) (0) | 2023.08.15 |
[Java] Stream 중간 연산 (Stream Intermediate Operation) (0) | 2023.08.03 |
[Java] Stream 생성 (파일 데이터 제외) (0) | 2023.08.03 |
[Java] Stream API (0) | 2023.08.03 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!