1. collect()와 Collectors()
collect()는 스트림의 요소들을 컬렉션으로 변환하거나, 요소들을 그룹화, 집계, 문자열로 결합하는 등의 작업을 수행하는데 사용 됨
‣ collect()는 Collector를 매개변수로 하는 스트림의 최종연산 (map은 중간연산)
Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로
Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) // 잘 안쓰임
‣ Collector는 수집(collect)에 필요한 메서드를 정의해 놓은 인터페이스
public interface Collector<T, A, R> {
Supplier<A> supplier(); // StringBuilder::new
BiConsume<A, T> accumulator() // (sb, s) -> sb.append(s)
BinaryOperator<A> combiner(); // (sb1, sb2) -> sb1.append(sb2) 결합방법 (병렬)
Function<A, R> finisher() // sb -> sb.toString()
Set<Characteristics> characteristics(); // 컬렉터의 특성이 담긴 Set을 반환
}
‣ Collectors 클래스는 다양한 기능의 컬렉터(Collector를 구현한 클래스)를 제공
List<String> fruitsList = fruitsStream.collect(Collectors.toList());
collect는 최종연산, Collector는 인터페이스, Collectors는 collect를 구현한 구현체를 제공
2. 스트림을 컬렉션, 배열로 변환
‣ 스트림을 컬렉션으로 변환 - toList(), toSet(), toMap(), toCollection()
List<String> names = stuStream.map(Student::getName).collect(Collectors.toList()); // Stream<Student>->Stream<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>
‣ 스트림을 배열로 변환 - toArray()
Student[] stuNames = studentStream.toArray(Student[]::new); // OK
Student[] stuNames = studentStream.toArray(); // 에러
Object[] stuNames = studentStream.toArray(); // OK
- 배열과 컬렉션 차이점
• 배열: 배열은 한 번 생성되면 크기를 변경할 수 없다. 즉, 배열의 크기는 고정되어 있으며 요소를 추가하거나 삭제할 수 없다.
• 컬렉션: 컬렉션은 동적으로 크기가 변경될 수 있다. 요소를 추가하거나 삭제하여 컬렉션의 크기를 유동적으로 조절할 수 있다.
3. 스트림의 통계 - counting(), summingInt()
‣ 스트림의 통계정보 제공 - counting(), summingInt(), maxBy(), minBy(), ...
long count = stuStream.count();
long count = stuStream.collect(counting()); // Collectors.counting(), collect는 그룹별로 나눌 때 유용
Ex)
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Student> students = List.of(
new Student("Alice", Gender.FEMALE, 85),
new Student("Bob", Gender.MALE, 70),
new Student("Charlie", Gender.MALE, 90),
new Student("David", Gender.MALE, 80),
new Student("Eva", Gender.FEMALE, 95)
);
// 성별(Gender)에 따라 그룹화하여 성적(grade)을 리스트로 모음
Map<Gender, List<Integer>> gradesByGender = students.stream()
.collect(Collectors.groupingBy(Student::getGender,
Collectors.mapping(Student::getGrade, Collectors.toList())));
// 결과 출력
gradesByGender.forEach((gender, grades) -> {
System.out.println(gender + " students' grades: " + grades);
});
}
}
enum Gender {
MALE, FEMALE
}
class Student {
private String name;
private Gender gender;
private int grade;
public Student(String name, Gender gender, int grade) {
this.name = name;
this.gender = gender;
this.grade = grade;
}
public String getName() {
return name;
}
public Gender getGender() {
return gender;
}
public int getGrade() {
return grade;
}
}
4. 스트림을 리듀싱 - reducing()
스트림의 요소들을 감소시키는데 유용, 여러 연산을 하나로 축소하거나, 합계, 곱셈, 최댓값, 최솟값 등의 작업 가능
‣ 스트림을 리듀싱 - reducing()
Collector reducing(BinaryOperator<T> op)
Collector reducing(T identity, BinaryOperator<T> op)
- BinaryOperator<T>: 같은 타입 T의 두 인수를 받아서 연산을 수행하고 결과를 반환하는 함수
- T identity: 스트림이 비어있을 때 반환될 초기값입니다.
public class Main {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
// Collectors.reducing을 사용하여 정수들의 합 구하기
Optional<Integer> sumOptional = numbers.stream()
.collect(Collectors.reducing((a, b) -> a + b));
int sum = sumOptional.orElse(0); // 기본값 0으로 설정
System.out.println("Sum: " + sum);
}
}
‣ 문자열 스트림의 요소를 모두 연결 - joining()
String studentNames = stuStream.map(Student::getName).collect(joining));
String studentNames = stuStream.map(Student::getName).collect(joining(","))); // 구분자
String studentNames = stuStream.map(Student::getName).collect(joining(",", "[", "]")); // [김자바, 이자바, ..]
5. 스트림의 그룹화와 분할
‣ partitioningBy()는 스트림을 2분할한다.
Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downstream)
• Predicate predicate : 스트림의 요소를 받아서 true 또는 false를 반환하는 함수로, 조건에 따라 요소가 참인 그룹과 거짓인 그룹으로 나누는 데 사용 된다.
• Collector downstream : 두 그룹에 대해 적용할 추가적인 컬렉터이다. 이 컬렉터를 사용하여 각 그룹의 요소들을 더 세분화하거나 통계를 구할 수 있다.
Map<Boolean, List<Student>> stuByGender = stuStream.collect(partitioningBy(Student::isMale)); // 학생들을 성별로 분할
List<Student> maleStudent = stuByGender.get(true); // 남학생 목록
List<Sutdent> femaleStudent = stuByGender.get(false); // 여학생 목록
Map<Boolean, Long> stuNumByGender = stuStream.collect(partitioningBy(Student::isMale, counting())); // 분할 + 통계
‣ groupingBy()는 스트림을 n분할한다.
Map<Integer, List<Student>> stuByBan = stuStream.collect(groupingBy(Student::getBan, toList()));
학생들을 반별로 나눌 때 쓰임
Map<Integer, Map<Integer, List<Student>>> stuByHakANdBan = stuStream.collect(groupingBy(Student::getHak, groupingBy(Student::getBan)))
학년, 반별로 나눌 때 Map안에 Map을 넣는 방식으로 가능
6. 스트림의 변환 표
'자바 스터디' 카테고리의 다른 글
[자바의 정석] CH 16 (0) | 2023.08.19 |
---|---|
[자바의 정석] CH10 1,2 (0) | 2023.08.13 |
[자바의 정석] CH13.8 쓰레드의 실행제어 (0) | 2023.07.30 |
[자바의 정석] CH 12 (Generics, Enum, Annotation) (0) | 2023.07.20 |
[자바의 정석] ch11.9 ~ ch.11.11 (0) | 2023.07.12 |