Ch14-50~55. 스트림 그룹화...
0. 목차
Chapter14. 람다와 스트림
Ch14 - 50. 스트림의 그룹화와 분할
Ch14 - 51. 스트림의 분할 : partitioningBy()
Ch14 - 52. 스트림의 분할 : partitioningBy() 예제
Ch14 - 53. 스트림의 그룹화 : groupingBy()
Ch14 - 54. 스트림의 그룹화 : groupingBy() 예제
Ch14 - 54. 스트림의 변환
Ch14 - 50. 스트림의 그룹화와 분할
▶ collectors.paritioningBy()
▷ 스트림을 2분할
Collector paritioningBy(Predicate predicate)
Collector paritioningBy(Predicate predicate, Collector downstream)
▶ collectors.guopingBy()
▷ 스트림을 n분할
Collector guopingBy(Function classifier)
Collector guopingBy(Function classifier, Collector downstream)
Collector guopingBy(Function classifier, Supplier mapFactory, Collector downstream)
Ch14 - 51. 스트림의 분할 : partitioningBy()
▶ 남학생 여학생 분할
▷ 성별 분할
// stuBySex = [key : boolean], [value : StudentList]
Map<Boolean, List<Student>> stuBySex
= stuStream
.collect(partitioningBy(Student::isMale))
▷ Map에서 남학생/여학생 목록을 얻음
// [key : stuBySex가 true = 남학생], [value : 남학생 List] → 남학생 List
List<Student> maleStudent = stuBySex.get(true);
// [key : stuBySex가 false = 여학생], [value : 여학생 List] → 여학생 List
List<Student> maleStudent = stuBySex.get(false);
▶ 남학생 여학생 수 구하기
▷ 성별 분할 + 통계
// stuNumBySex = [key : boolean], [value : counting()]
Map<Boolean, Long> stuNumBySex
= stuStream
.collect(partitioningBy(Student::isMale), counting())
▷ Map에서 남학생/여학생 수 얻음
// [key : stuNumBySex true = 남학생], [value : 남학생 count] → 남학생 수
System.out.println("남학생 수 : " + stuNumBySex.get(true));
// [key : stuNumBySex false = 여학생], [value : 여학생 count] → 여학생 수
System.out.println("여학생 수 : " + stuNumBySex.get(false));
▶ 남학생 1등/여학생 1등 구하기
▷ 성별 분할 + 통계
// topScoreBySex = [key : boolean], [value : maxBy()]
Map<Boolean, Optional<Student>> topScoreBySex
= stuStream
.collect(partitioningBy(Student::isMale), maxBy(comparingInt(Student::getScore))));
▷ 남학생 1등/여학생 1등 구하기
// [key : topScoreBySex true = 남학생], [value : 남학생 maxBy] → 남학생 1등
System.out.println("남학생 1등 : " + topScoreBySex.get(true));
// [key : topScoreBySex false = 여학생], [value : 여학생 maxBy] → 남학생 1등
System.out.println("여학생 1등 : " + topScoreBySex.get(false));
▶ 다중 분할
▷ 성별 분할 + 합격/불합격 분할
// topScoreBySex = [key : boolean], [value : key(boolean), value(StudentList)]
Map<Boolean, Map<Boolean, List<Student>>> failedStuBySex
= stuStream
.collect(partitioningBy(Student::isMale), partitioningBy(s -> s.getScore() < 150)); // score가 150 이하면, failed
▷ 남학생 불합격자/여학생 불합격자 구하기
// [key : failedStuBySex true = 남학생], [value : key(남학생), value(s -> s.getScore() < 150)] → 남학생 불합격자
List<Student> failedStuBySex = failedStuBySex.get(true).get(true);
// [key : failedStuBySex false = 여학생], [value : key(여학생), value(s -> s.getScore() < 150)] → 여학생 불합격자
List<Student> failedStuBySex = failedStuBySex.get(false).get(true);
Ch14 - 52. 스트림의 분할 : partitioningBy() 예제
▶ Student 클래스 생성
▷ 이름, 성별, 학년, 반, 성적 변수 생성
class Student {
String name; // 이름
boolean isMale; // 성별
int hak; // 학년
int ban; // 반
int score; // 성적
...
}
▷ 이름, 성별, 학년, 반, 성적 변수 생성을 매개 변수로 받는 생성자
class Student {
...
Student(String name, boolean isMale, int hak, int ban, int score) {
this.name = name;
this.isMale = isMale;
this.hak = hak;
this.ban = ban;
this.score = score;
}
...
}
▷ get…() 반환값 설정
class Student {
...
String getName() { return name; }
boolean isMale() { return isMale; }
int getHak() { return hak; }
int getBan() { return ban; }
int getScore() { return score; }
...
}
▷ toString() 오버라이딩
class Student {
...
public String toString() {
return String.format("[%s, %s, %d학년 %d반, %3d점]", name, isMale ? "남" : "여", hak, ban, score);
}
...
}
▶ Act 클래스 생성
▷ Student[] stuArr 생성
class Act {
public static void main(String[] args) {
Student[] stuArr
= { new Student("나자바", true, 1, 1, 300),
new Student("김지미", false, 1, 1, 250),
new Student("김자바", true, 1, 1, 200),
new Student("이지미", false, 1, 2, 150),
new Student("남자바", true, 1, 2, 100),
new Student("안지미", false, 1, 2, 50),
new Student("황지미", false, 1, 3, 100),
new Student("강지미", false, 1, 3, 150),
new Student("이자바", true, 1, 3, 200),
new Student("나자바", true, 2, 1, 300),
new Student("김지미", false, 2, 1, 250),
new Student("김자바", true, 2, 1, 200),
new Student("이지미", false, 2, 2, 150),
new Student("남자바", true, 2, 2, 100),
new Student("안지미", false, 2, 2, 50),
new Student("황지미", false, 2, 3, 100),
new Student("강지미", false, 2, 3, 150),
new Student("이자바", true, 2, 3, 200)
};
...
}
}
▷ 성별 분할
class Act {
public static void main(String[] args) {
...
System.out.printf("1. 성별 분할%n");
Map<Boolean, List<Student>> stuBySex
= Stream
.of(stuArr)
.collect(partitioningBy(Student::isMale));
List<Student> maleStudent = stuBySex.get(true);
List<Student> femaleStudent = stuBySex.get(false);
System.out.println("maleStudent");
for (Student s : maleStudent)
System.out.println(s);
System.out.println("femaleStudent");
for (Student s : femaleStudent)
System.out.println(s);
...
}
}
// console
1. 성별 분할
maleStudent
[나자바, 남, 1학년 1반, 300점]
[김자바, 남, 1학년 1반, 200점]
[남자바, 남, 1학년 2반, 100점]
[이자바, 남, 1학년 3반, 200점]
[나자바, 남, 2학년 1반, 300점]
[김자바, 남, 2학년 1반, 200점]
[남자바, 남, 2학년 2반, 100점]
[이자바, 남, 2학년 3반, 200점]
femaleStudent
[김지미, 여, 1학년 1반, 250점]
[이지미, 여, 1학년 2반, 150점]
[안지미, 여, 1학년 2반, 50점]
[황지미, 여, 1학년 3반, 100점]
[강지미, 여, 1학년 3반, 150점]
[김지미, 여, 2학년 1반, 250점]
[이지미, 여, 2학년 2반, 150점]
[안지미, 여, 2학년 2반, 50점]
[황지미, 여, 2학년 3반, 100점]
[강지미, 여, 2학년 3반, 150점]
▷ 남학생 여학생 수 구하기
class Act {
public static void main(String[] args) {
...
System.out.printf("%n2. 남학생/여학생 수%n");
Map<Boolean, Long> stuNumBySex
= Stream
.of(stuArr)
.collect(partitioningBy(Student::isMale, counting()));
System.out.println("남학생 수 :" + stuNumBySex.get(true));
System.out.println("여학생 수 :" + stuNumBySex.get(false));
...
}
}
// console
2. 남학생/여학생 수
남학생 수 :8
여학생 수 :10
▷ 남학생 1등/여학생 1등 구하기
class Act {
public static void main(String[] args) {
...
System.out.printf("%n3. 남학생 1등/여학생 1등%n");
Map<Boolean, Optional<Student>> topScoreBySex
= Stream
.of(stuArr)
.collect(partitioningBy(Student::isMale, maxBy(comparingInt(Student::getScore))));
System.out.println("남학생 1등 :" + topScoreBySex.get(true));
System.out.println("여학생 1등 :" + topScoreBySex.get(false));
Map<Boolean, Student> topScoreBySex2
= Stream
.of(stuArr)
.collect(partitioningBy
(Student::isMale,
collectingAndThen(maxBy(comparingInt(Student::getScore)),
Optional::get))); // Optional 안에 있는 걸 꺼내서 반환
System.out.println("남학생 1등 :" + topScoreBySex2.get(true));
System.out.println("여학생 1등 :" + topScoreBySex2.get(false));
...
}
}
// console
3. 남학생 1등/ 여학생 1등
남학생 1등 :Optional[[나자바, 남, 1학년 1반, 300점]]
여학생 1등 :Optional[[김지미, 여, 1학년 1반, 250점]]
// Optional::get
남학생 1등 :[나자바, 남, 1학년 1반, 300점]
여학생 1등 :[김지미, 여, 1학년 1반, 250점]
▷ 다중 분할(성별 분할, 합격자/불합격자 분할)
class Act {
public static void main(String[] args) {
...
System.out.printf("%n4. 다중 분할(성별 분할, 합격자/불합격자 분할)%n");
Map<Boolean, Map<Boolean, List<Student>>> failedStuBySex
= Stream
.of(stuArr)
.collect(partitioningBy(Student::isMale, partitioningBy(s -> s.getScore() <= 100))); // 100점 이하 불합격
List<Student> failedMaleStu = failedStuBySex.get(true).get(true);
List<Student> failedFemaleStu = failedStuBySex.get(false).get(true);
System.out.println("failedMaleStu");
for (Student s : failedMaleStu)
System.out.println(s);
System.out.println("failedFemaleStu");
for (Student s : failedFemaleStu)
System.out.println(s);
}
}
// console
4. 다중 분할(성별 분할, 합격자/불합격자 분할)
failedMaleStu
[남자바, 남, 1학년 2반, 100점]
[남자바, 남, 2학년 2반, 100점]
failedFemaleStu
[안지미, 여, 1학년 2반, 50점]
[황지미, 여, 1학년 3반, 100점]
[안지미, 여, 2학년 2반, 50점]
[황지미, 여, 2학년 3반, 100점]
Ch14 - 53. 스트림의 그룹화 : groupingBy()
▶ 반별 분할
▷ 학생을 반별로 그룹화
// [key : 반], [value : StudentList]
Map<Integer, List<Student>> stuByBan
= stuStream
.collect(groupingBy(getBan, toList())); // toList() 생략 가능
▶ 다중 그룹화
▷ 학년별 + 반별 그룹화
// [key : 학년], [value : key(반), value(StudentList)]
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan
= stuStream
.collect(groupingBy(Student::getHak, // 학년별 그룹화
groupingBy(Student::getBan))); // 반별 그룹화
▶ 다중 그룹화
▷ 학년별 + 반별 + 성적별 그룹화
// [key : 학년], [value : key(반), value(StudentLevel)]
Map<Integer, Map<Integer, Set<Student.Level>>> stuByHakAndBan
= stuStream
.collect(groupingBy(Student::getHak, // 학년별 그룹화
groupingBy(Student::getBan, // 반별 그룹화
mapping(s -> {
if (s.getScore() >= 200) { return Student.Level.HIGH; } // HIGH Level
else if (s.getScore() >= 100) { return Student.Level.MID; } // MID Level
else { return Student.Level.Low; } // Low Level
}, toSet()) // mapping() END
)) // groupingBy() END
); // collect() END
map()
나눌 게 좀 많다 싶을 때 map() 안에 if문을 사용하여 그룹화
Ch14 - 54. 스트림의 그룹화 : groupingBy() 예제
▶ Student 클래스 생성
▷ 이름, 성별, 학년, 반, 성적 변수 생성
class Student {
String name; // 이름
boolean isMale; // 성별
int hak; // 학년
int ban; // 반
int score; // 성적
...
}
▷ 이름, 성별, 학년, 반, 성적 변수 생성을 매개 변수로 받는 생성자
class Student {
...
Student(String name, boolean isMale, int hak, int ban, int score) {
this.name = name;
this.isMale = isMale;
this.hak = hak;
this.ban = ban;
this.score = score;
}
...
}
▷ get…() 반환값 설정
class Student {
...
String getName() { return name; }
boolean isMale() { return isMale; }
int getHak() { return hak; }
int getBan() { return ban; }
int getScore() { return score; }
...
}
▷ toString() 오버라이딩
class Student {
...
public String toString() {
return String.format("[%s, %s, %d학년 %d반, %3d점]", name, isMale ? "남" : "여", hak, ban, score);
}
...
}
▷ Level을 그룹화 하는 메서드 생성
class Student {
...
enum Level {
HIGH, MID, LOW
}
}
▶ Act 클래스 생성
▷ Student[] stuArr 생성
class Act {
public static void main(String[] args) {
Student[] stuArr = {
new Student("나자바", true, 1, 1, 300),
new Student("김지미", false, 1, 1, 250),
new Student("김자바", true, 1, 1, 200),
new Student("이지미", false, 1, 2, 150),
new Student("남자바", true, 1, 2, 100),
new Student("안지미", false, 1, 2, 50),
new Student("황지미", false, 1, 3, 100),
new Student("강지미", false, 1, 3, 150),
new Student("이자바", true, 1, 3, 200),
new Student("나자바", true, 2, 1, 300),
new Student("김지미", false, 2, 1, 250),
new Student("김자바", true, 2, 1, 200),
new Student("이지미", false, 2, 2, 150),
new Student("남자바", true, 2, 2, 100),
new Student("안지미", false, 2, 2, 50),
new Student("황지미", false, 2, 3, 100),
new Student("강지미", false, 2, 3, 150),
new Student("이자바", true, 2, 3, 200)
};
...
}
}
▷ 반별 그룹화
class Act {
public static void main(String[] args) {
...
System.out.printf("1. 반별 그룹화%n");
Map<Integer, List<Student>> stuByBan
= Stream
.of(stuArr)
.collect(groupingBy(Student::getBan));
for (List<Student> ban : stuByBan.values()) {
for (Student s : ban) {
System.out.println(s);
}
}
...
}
}
// console
1. 반별 그룹화
[나자바, 남, 1학년 1반, 300점]
[김지미, 여, 1학년 1반, 250점]
[김자바, 남, 1학년 1반, 200점]
[나자바, 남, 2학년 1반, 300점]
[김지미, 여, 2학년 1반, 250점]
[김자바, 남, 2학년 1반, 200점]
[이지미, 여, 1학년 2반, 150점]
[남자바, 남, 1학년 2반, 100점]
[안지미, 여, 1학년 2반, 50점]
[이지미, 여, 2학년 2반, 150점]
[남자바, 남, 2학년 2반, 100점]
[안지미, 여, 2학년 2반, 50점]
[황지미, 여, 1학년 3반, 100점]
[강지미, 여, 1학년 3반, 150점]
[이자바, 남, 1학년 3반, 200점]
[황지미, 여, 2학년 3반, 100점]
[강지미, 여, 2학년 3반, 150점]
[이자바, 남, 2학년 3반, 200점]
▷ 성적별 그룹화
class Act {
public static void main(String[] args) {
...
System.out.printf("%n2. 성적별로 그룹화%n");
Map<Student.Level, List<Student>> stuByLevel
= Stream
.of(stuArr)
.collect(groupingBy(s -> {
if (s.getScore() >= 200)
return Student.Level.HIGH;
else if (s.getScore() >= 100)
return Student.Level.MID;
else
return Student.Level.LOW;
}));
// Level 별로 출력
TreeSet<Student.Level> keySet
= new TreeSet<>(stuByLevel.keySet());
for (Student.Level key : keySet) {
System.out.println("[" + key + "]");
for (Student s : stuByLevel.get(key))
System.out.println(s);
System.out.println();
}
...
}
}
// console
2. 성적별 그룹화
[HIGH]
[나자바, 남, 1학년 1반, 300점]
[김지미, 여, 1학년 1반, 250점]
[김자바, 남, 1학년 1반, 200점]
[이자바, 남, 1학년 3반, 200점]
[나자바, 남, 2학년 1반, 300점]
[김지미, 여, 2학년 1반, 250점]
[김자바, 남, 2학년 1반, 200점]
[이자바, 남, 2학년 3반, 200점]
[MID]
[이지미, 여, 1학년 2반, 150점]
[남자바, 남, 1학년 2반, 100점]
[황지미, 여, 1학년 3반, 100점]
[강지미, 여, 1학년 3반, 150점]
[이지미, 여, 2학년 2반, 150점]
[남자바, 남, 2학년 2반, 100점]
[황지미, 여, 2학년 3반, 100점]
[강지미, 여, 2학년 3반, 150점]
[LOW]
[안지미, 여, 1학년 2반, 50점]
[안지미, 여, 2학년 2반, 50점]
▷ 성적별 학생수
class Act {
public static void main(String[] args) {
...
System.out.printf("%n3. 성적별 학생수%n");
Map<Student.Level, Long> stuCntByLevel
= Stream
.of(stuArr)
.collect(groupingBy(s -> {
if (s.getScore() >= 200) { return Student.Level.HIGH; }
else if (s.getScore() >= 100) { return Student.Level.MID; }
else { return Student.Level.LOW; }
}, counting()));
for (Student.Level key : stuCntByLevel.keySet())
System.out.printf("[%s] - %d명, ", key, stuCntByLevel.get(key));
System.out.println();
/*
* for(List<Student> level : stuByLevel.values()) { System.out.println();
* for(Student s : level) { System.out.println(s); } }
*/
...
}
}
// console
3. 성적별 학생수
[LOW] - 2명, [HIGH] - 8명, [MID] - 8명,
▷ 다중 그룹화(학년별 + 반별)
class Act {
public static void main(String[] args) {
...
System.out.printf("%n4. 다중 그룹화(학년별 + 반별)");
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan
= Stream
.of(stuArr)
.collect(groupingBy(Student::getHak, groupingBy(Student::getBan)));
for (Map<Integer, List<Student>> hak : stuByHakAndBan.values()) {
for (List<Student> ban : hak.values()) {
System.out.println();
for (Student s : ban) { System.out.println(s); }
}
}
...
}
}
// console
4. 다중 그룹화(학년별 + 반별)
[나자바, 남, 1학년 1반, 300점]
[김지미, 여, 1학년 1반, 250점]
[김자바, 남, 1학년 1반, 200점]
[이지미, 여, 1학년 2반, 150점]
[남자바, 남, 1학년 2반, 100점]
[안지미, 여, 1학년 2반, 50점]
[황지미, 여, 1학년 3반, 100점]
[강지미, 여, 1학년 3반, 150점]
[이자바, 남, 1학년 3반, 200점]
[나자바, 남, 2학년 1반, 300점]
[김지미, 여, 2학년 1반, 250점]
[김자바, 남, 2학년 1반, 200점]
[이지미, 여, 2학년 2반, 150점]
[남자바, 남, 2학년 2반, 100점]
[안지미, 여, 2학년 2반, 50점]
[황지미, 여, 2학년 3반, 100점]
[강지미, 여, 2학년 3반, 150점]
[이자바, 남, 2학년 3반, 200점]
▷ 다중 그룹화 + 통계(학년별 + 반별 1등)
class Act {
public static void main(String[] args) {
...
System.out.printf("%n5. 다중 그룹화 + 통계(학년별 + 반별 1등)%n");
Map<Integer, Map<Integer, Student>> topStuByHakAndBan
= Stream
.of(stuArr)
.collect(groupingBy(Student::getHak, groupingBy(Student::getBan,
collectingAndThen(maxBy(comparingInt(Student::getScore)), Optional::get))));
for (Map<Integer, Student> ban : topStuByHakAndBan.values()) {
for (Student s : ban.values()) { System.out.println(s); }
}
...
}
}
// console
5. 다중 그룹화 + 통계(학년별 + 반별 1등)
[나자바, 남, 1학년 1반, 300점]
[이지미, 여, 1학년 2반, 150점]
[이자바, 남, 1학년 3반, 200점]
[나자바, 남, 2학년 1반, 300점]
[이지미, 여, 2학년 2반, 150점]
[이자바, 남, 2학년 3반, 200점]
▷ 다중 그룹화 + 통계(학년별 + 반별 성적 그룹)
class Act {
public static void main(String[] args) {
...
System.out.printf("%n6. 다중 그룹화 + 통계(학년별 + 반별 성적 그룹)%n");
Map<String, Set<Student.Level>> stuByScoreGroup
= Stream
.of(stuArr)
.collect(groupingBy(s -> s.getHak() + "-" + s.getBan(),
mapping(s -> {
if (s.getScore() >= 200) { return Student.Level.HIGH; }
else if (s.getScore() >= 100) { return Student.Level.MID; }
else { return Student.Level.LOW; }
}, toSet()
)
));
Set<String> keySet2
= stuByScoreGroup
.keySet();
for (String key : keySet2) {
System.out.println("[" + key + "]" + stuByScoreGroup.get(key));
}
}
}
// console
6. 다중 그룹화 + 통계(학년별 + 반별 성적그룹)
[1-1][HIGH]
[2-1][HIGH]
[1-2][LOW, MID]
[2-2][LOW, MID]
[1-3][HIGH, MID]
[2-3][HIGH, MID]
Ch14 - 55. 스트림의 변환