int max(int a, int b) {
return a > b ? a : b;
}
--- 람다식 ---
(a, b) -> a > b ? a : b
int max(int a, int b) {
return a > b ? a : b;
}
--- 익명 함수 ---
int max(int a, int b) -> {
return a > b ? a : b;
}
근본적으로 동일. 함수는 일반적 용어, 메서드는 객체지향개념 용어
함수는 클래스에 독립적, 메서드는 클래스에 종속적
int max(int a, int b) {
return a > b ? a : b;
}
--- 람다식 ---
int max(int a, int b) -> {
return a > b ? a : b;
}
(int a, int b) -> {
return a > b ? a : b;
}
--- 람다식 ---
(int a, int b) -> a > b ? a : b
(int a, int b) -> a > b ? a : b
--- 람다식 ---
(a, b) -> a > b ? a : b
(a) -> a * a
(int a) -> a * a
--- 람다식 ---
a -> a * a // OK
int a -> a * a // 에러
(int i) -> {
System.out.println(i);
}
--- 람다식 ---
(int i) -> System.out.println(i)
단, 하나뿐인 문장이 return문이면 괄호{} 생략불가
(int a, int b) -> { return a > b ? a : b; } // OK
(int a, int b) -> return a > b ? a : b // 에러
메서드 | 람다식 |
---|---|
int max(int a, int b) { return a > b ? a : b; } | (a, b) -> a > b ? a : b |
int printVar(String name, int i) { System.out.println(name+"="+i); } | (name, i) -> System.out.println(name+"="+i) |
int squars(int x) { return x * x; } | x -> x * x |
int roll() { return (int)(Math.random() * 6}; } | () -> (int)(Math.random() * 6) |
람다식은 익명함수가 아니라 익명 객체이다.
(a, b) -> a > b ? a : b
-------------
new Object() {
int max(int a, int b) {
return a > b ? a : b;
}
}
람다식(익명 객체)을 다루기 위한 참조변수가 필요.
Object obj = new Object() { int max(int a, int b) { return a > b ? a : b; } }; --- 람다식 --- 타입 obj = (a, b) -> a > b ? a : b ; // 어떤타입? int value = obj.max(3, 5); // 에러. Object클래스에 max()가 없음
interface Myfunction {
public abstract int max(int a, int b);
}
----------------------------
MyFunction f = new Myfunction() {
public int max(int a, int b) {
return a > b ? a : b;
}
}
int value = f.max(3, 5); // OK. MyFunction에 max()가 있음
MyFunction f = (a, b) -> a > b ? a : b;
int value = f.max(3, 5); // 실제로는 람다식(익명 함수)이 호출됨
void aMethod(MyFunction f) {
f.myMethod(); // MyFunction에 정의된 메서드 호출
}
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);
--- 위 두식을 하나로 합치면 ---
aMethod( () -> System.out.println("myMethod()"));
MyFunction myMethod() { // 무려 람다식이 반환이 된다고 ㄷㄷ
MyFunction f = ()->{};
return f;
}
--- 위를 줄여서 표현 ---
MyFunction myMethod() {
return ()->{};
}
일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의함
함수형 인터페이스 | 메서드 | 설명 |
---|---|---|
java.lang. Runnable | void run() | 매개변수도 없고, 반환값도 없음. |
Supplier<T> | T get() | 매개변수는 없고, 반환값만 있음. |
Consumer<T> | void accept(T t) | Supplier와 반대로 매개변수만 있고, 반환값이 없음 |
Function<T,R> | R apply(T t) | 일반적인 함수. 하나의 매개변수를 받아서 결과를 반환 |
Predicate<T> | boolean test(T t) | 조건식을 표현하는데 사용됨. 매개변수는 하나, 반환 타입은 boolean |
// Predicate는 조건식을 쓸때 사용하는 함수인터페이스
Predicate<String> isEmptyStr = s -> s.length()==0;
Strings = "";
if(isEmptyStr.test(s)) // if(s.length()==0)
System.out.println("This is an empty String.");
함수형 인터페이스 | 메서드 | 설명 |
---|---|---|
BiConsumer<T,U> | void accept(T t, U u) | 두개의 매개변수만 있고, 반환값이 없음 |
BiPredicate<T,U> | boolean test(T t, U u) | 조건식을 표현하는데 사용됨. 매개변수는 둘, 반환값은 boolean |
BiFunction<T,U,R> | R apply(T t, U u) | 두 개의 매개변수를 받아서 하나의 결과를 반환 |
매개변수가 2개면 앞에 Bi가 붙는다.
두 개보다 많은 매개변수는 직접 만들어야 한다.
// 요렇게
@FunctionalInterface
interface HolyMyGod<T,U,V,R> {
R apply(T t, U u, V v);
}
매개변수의 타입과 반환타입의 타입이 모두 일치하는 함수도 있다.
함수형 인터페이스 | 메서드 | 설명 |
---|---|---|
UnaryOperator<T> | T apply(T t) | Function의 자손, Function과 달리 매개변수와 결과의 타입이 같다. |
BinaryOperator<T> | T apply(T t, T t) | BiFunction의 자손, BiFunction과 달리 매개변수와 결과의 타입이 같다. |
여러개의 조건식을 합쳐서 하나로 가능!
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i%2 == 0;
Predicate<Integer> notP = p.negate(); //i >= 100 반대의 경우인가. 음
// 100 <= i && (i < 200 || i%2==0)
Predicate<Integer> all = notP.and(q.or(r));
System.out.println(all.test(150)); // true
--- 아래처럼 람다식 직접 꾸역꾸역 넣어도 됨 ---
Predicate<Integer> all = notP.and(i -> i < 200).or(i -> i%2 == 0);
그리고 뜬금없긴한데 isEqual()라는것도 사용할 수 있음
// str1과 str2가 같은지 비교
boolean result = Predicate.isEqual(str1).test(str2);
자, 컬렉션 프레임웍에서도 함수형 인터페이스가 있다. 내용은 대충 어떤 함수가 있고 어떤 기능을 하는지 나온게 끝. 그리고 소개 안할 예정이다. 이유는 아직 쓰는법을 모르고, 필요한 이유도 모르기 때문에 괜히 머리에 집어넣기 그렇기 때문에. 그냥 넘어가겠다 이건. 호옥시나 나중에 찾아와서 이걸 찾는다면 책 564페이지에 있다.
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
// 위는 람다식이고 이를 좀더 편하게 메서드 참조로 나타낼 수 있다는 뜻
Function<String, Integer> f = Integer::parseInt; // 메서드 참조
Supplier<MyClass> s = () -> new MyClass(); //람다식
Supplier<MyClass> s = MyClass::new; //메서드 참치
Function<Integer, MyClass> f = (i) -> new MyClass(i); // 람다식
Function<Integer, MyClass> f2 = MyClass::new; // 메서드 참조
// 람다식
BiFunction<Integer, String, MyClass> bf = (i, s) -> new MyClass(i, s);
// 메서드 참조
BiFunction<Integer, String, MyClass> bf2 = MyClass::new;
// 배열 람다식
Function<Integer, int[]> f = x -> new int[x];
// 배열 메서드 참조
Function<integer, int[]> f2 = int[]::new
이게 뭘까 간단히 말해본다. 각 클래스들에게는 비슷한 기능들이 중복되어 있는 경우가 많다. 예를들어 List를 정렬할 때는 Collections.sort(), 배열을 정렬할때는 Arrays.sort()를쓴다. 이걸 모두 같은 방식으로 다루기 위해 나온게 스트림.
String[] strArr = {"aaa", "ddd", "ccc"};
List<String> strList = Arrays.asList(strArr);
Stream<String> strStream1 = strList.stram(); // 스트림 생성
Stream<String> strStream2 = Arrays.stream(strArr); // 스트림 생성
strStream1.sorted().forEach(System.out::println);
strStream2.sorted().forEach(System.out::println);
읽기만하고 변경은 하지않는다. 필요하다면 밑에처럼 해야된다.
List<String> sortedList = strSteram2.sorted().collect(Collectors.toList());
한번 사용하면 닫혀서 다시 못쓴다. 필요하면 다시 생성해야됨.
stream.forEach(System.out::println);
--- 모든 요소를 거쳐간다. ---
for(String str : strList)
System.out.println(str);
아무튼 그래서 간결하다는 뜻.
int sum = strStream.parallel() // strStream을 병렬 스트림으로 전환
.mapToInt(s -> s.length())
.sum();
Stream<E> stream() // Collection 인터페이스의 메서드
--- 예시 ---
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // 가변인자
Stream<Integer> intStream = list.stream(); // list를 소스로 하는 스트림 생성
intStream.forEach(System.out::println); // 스트림의 모든 요소를 출력한다.
intStream.forEach(System.out::println); // 에러 스트림 닫힘.
Stream<T> Stream.of(T... values) // 가변인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startIncludesive, int endExclusive)
Stream<T> strStream=Streamof("a", "b", "c"); // 가변인자
Stream<T> strSteram=Streamof(new String[]{"a", "b", "c"});
Stream<T> strStream=Arrays.stream(new String[]{"a", "b", "c"});
Stream<T> strStream=Arrays.stream(new String[]{"a", "b", "c"}, 0, 3);
Random클래스에는 아래와 같은 인스턴스 메서드들이 포함되어 있다.
IntStream ints()
LongStream longs()
DoubleStream doubles()
--- 무한 스트림은 limit()을 같이 데리고 다닌다 ---
IntStream intStream = new Random().ints(); // 한무 스트림
intStream.limit(5).forEach(System.out::println); // 5개의 요소만 출력한다.
--- 유한 스트림은 그냥 쓴다. ---
IntStream intStream = new Random().ints(5); // 크기가 5인 난수 스트림을 반환
long도되고 double도 되고
시작 부터 끝까지 연속으로 수를 생성한다. range()는 end를 포함하지 않고 rangeClosed()는 end까지 포함.
IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end)
IntStream intStream = IntStream.range(1, 5) // 1, 2, 3, 4
IntStream intStream = IntStream.rangeClosed(1, 5) // 1, 2, 3, 4, 5
이것도 long도 되고 double도 되고
람다식을 매개변수로 받아서, 이 람다식에 의해 계산되는 값들을 요소로 하는 무한 스트림 생성
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
static <T> Stram<T> generate(Supplier<T> s)
// seed값을 람다식에 넣고 나온값을 다시 람다식에 넣는 것을 반복
Stream<Integer> evenStream = Stream.iterate(0, n->n+2); // 0, 2, 4, 6, ...
//generate도 람다식에 무한 스트림을 생성하지만 이전값을 사용하지 않는다.
Stream<Double> randomStram = Stream.generate(Math::random);
Stream<Integer> oneStream = Stream.generate(()->1);
// 그리고 generate는 매개변수 못받음
IntStream evenStream = Stream.iterate(0, n->n+2); // 에러
DoubleStream randomStream = Stream.generate(Math::random); // 에러
list()는 지정된 디렉토리(dir)에 있는 파일의 목록을 소스로 하는 스트림을 생성해서 반환한다.
Stream<Parh> Files.list(Path dir)
Stream<String> Files.lines(Parh parh)
Stream<String> Files.lines(Path path, Charst cs)
스트림에 연산을 수행한 결과가 하나도 없을 때, null보다 빈 스트림을 반환하는 것이 낫다.
Sream emptyStream = Stream.empty(); // empty()는 빈 스트림을 생성해서 반환한다.
long cout = emptyStream.count(); // count의 값은 0
중간 연산 : 연산 결과가 스트림인 연산. 스트림에 연속해서 중간 연산할 수 있음
최종 연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한번만 가능
skip(3)은 처음 3개의 요소를 건너뛰고 limit(5)는 요소를 5개로 제한한다.
Stream<T> skip(long n)
Stream<T> limit(long maxSize)
IntStream intStream = IntSream.rangeClosed(1, 10); // 1~10의 요소를 가진 스트림
intStream.skip(3).limit(5).forEach(System.out::print); // 45678
distinct()는 스트림에서 중복된 요소들을 제거하고, filter()는 주어진 조건에 맞지 않는 요소를 걸러낸다.
Stream<T> filter(Predicate<? super T> predicate)
Stream<T> distinct()
IntStream intStream = IntStream.of(1, 2, 2, 3, 3, 3, 4, 5, 5, 6);
intStream.distinct().forEach(System.out::print); // 123456
IntStream intStream = IntStream.rangeClosed(1, 10); // 1~10
intStream.filter(i -> i%2 == 0).forEach(System.out::print); // 246810
정 렬!
Stream<T> sorted()
Stream<T> sorted(Comparator<? super T> comparator)
Sream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
strStream.sorted().forEach(System.out::print); // CCaaabccdd
문자열 스트림 정렬 방법 | 출력결과 |
---|---|
strStream.sorted() // 기본 정렬 strStream.sorted(Comparator.naturalOrder()) // 기본 정렬 strStream.sorted((s1, s2) -> s1.compareTo(s2)) // 람다식도 가능 strStream.sorted(String::compareTo) // 위의 문장과 동일 | CCaaabccdd |
strStream.sorted(Comparator.reverseOrder()) // 기본 정렬의 역순 strStream.sorted(Comparator.naturalOrder().reversed()) | ddccbaaaCC |
strStream.sorted(String.CASE_INSENSITIVE_ORDER) // 대소문자 구분안함 | aaabCCccdd |
strStream.sorted(String.CASE_INSENSITIVE_ORDER.reversed()) // 역순 | ddCCccbaaa |
strStream.sorted(Comparator.comparing(String::length)) // 길이 순 정렬 strStream.sorted(Comparator.comparingInt(String::length)) // no 오토박싱 | bddCCccaaa |
strStream.sorted(Comparator.comparing(String::length).reversed()) | aaaddCCccb |
이 메서드들은 모두 Comparator<T>
를 반환
comparing(Function<T, U> keyExtractor)
comparing(Function<T, U> keyExtractor, Comparator<U> keyComparator)
--- 정렬조건 추가 ---
thenComparing(Comparator<T> other)
thenComparing(Function<T, U> keyEtractor)
thenComparing(Function<T, U> keyExtractor, Comparator<U> keyComp)
--- 예시 ---
// 학생 스트림을 반, 성적순, 그리고 이름순으로 정렬하여 출력하려면
studentStream.sorted(Comparator.comparing(Student::getBan)
.thenComparing(Student::getTotalScore)
.thenComparing(Student::getName))
.forEach(System.out::println);
요소에 저장된 값중에서 원하는 필드만 뽑아내거나 특정형태로 변환
Stream<R> map(Function<? super T, ? extends R> mapper)
--- File에서 파일의 이름만 추출하고 싶을때 ---
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1"),
new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
// map()으로 Stream<File>을 Stream<String>으로 변환
Stream<String> filenameStream = fileStream.map(File::getName);
filenameStream.forEach(System.out::println); // 스트림의 모든 파일이름을 출력
--- 아니 이게 된다고? ---
fileStream.map(File::getName) // Stream<File> -> Stream<String>
.filter(s -> s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.map(s -> s.substring(s.indexOf('.')+1)) // Stream<String> -> Sream<String>
.map(String::toUpperCase) // 모두 대문자로 전환
.distinct() // 중복 제거
.forEach(System.out::print); // JAVABAKTXT
연산과 연산 사이에 올바르게 처리되었는지 확인가능스.
fileStream.map(File::getName)
.fileter(s -> s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.peek(s->System.out.printf("filename=%s%n", s)) // 파일명을 출력한다.
.map(s -> s.substring(s.indexOf('.')+1)) // 확장자만 추출
.peek(s->System.out.printf("extension=%s%n", s)) // 확장자를 출력한다.
.forEach(System.out::println);
아니 이거 왜쓰는거임 화나네. Stream<String[]>를 Stream 로 바꾸기 위해 쓰는거임
Stream<String[]> strArrStrm = Stream.of(
new String[]{"abc", "def", "ghi" },
new String[]{"ABC", "GHI", "JKLMN"}
};
// map 쓰면 일케됨
Stream<Stream<String>> strStrStrm = strArrStrm.map(Arrays::stream);
// flatMap쓰면 일케됨
Steam<String> strStrm = strArrStrm.flatMap(Arrays::stream);
Optional<T>
아니 이거 갑자기 왜나옴
public final class Optional<T> {
priavte final T value; // T타입의 참조변수
...
}
--- 객체 생성하기 ---
String str = "abc";
// 생성할 때는 of() 또는 ofNullable()을 사용한다.
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(new String("abc"));
// 참조변수 값이 null일 가능성이 있다면 ofNullable()
Optional<String> optVal = Optional.of(null); // NullPointerException 발생
Optional<String> optVal = Optional.ofNullable(null); // OK
// 빈 객체로 초기화하는 것이 바람직하다.
Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화
// get()을 사용하면 저장된 값을 가져올 수 있다.
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal에 저장된 값을 반환. null이면 예외발생
String str2 = optVal.orElse(""); // optVal에 저장된 값이 null일 때는, ""를 반환
orElse()의 변형으로는 null을 대체할 값을 반환하는 람다식을 지정할 수 있는 orElseGet()과 null일 때 지정된 예외를 발생시키는 orElseThrow()가 있다.
T orElseGet(Supplier<? extends T> other)
T orElseThrow(Supplier<? extends X> exceptionSupplier)
String str3=optVal2.orElseGet(String::new); // () -> new String()와 동일
String str4=optVal2.orElseThrow(NullPointerException::new); // 널이면 예외발생
ifPresent()는 Optional객체의 값이 null이면 false를 아니면 true 반환
// str이 null이 아닐 때만 값을 출력하고, null이면 아무 일도 일어나지 않는다.
Optional.ofNullable(str).ifPresent(System.out::println);
아니 이거 뜬금없이 갑자기 왜 나오는거야 아아악
OptionalInt findAny()
OptionalInt findFirst()
OptionalInt reduce(InBinaryOperator op)
OptionalInt max()
OptionalInt min()
OptionalDouble average()
--- 클래스에 따라 메서드가 달라요 ---
Optional<T> T get()
OptionalInt int getAsInt()
OptionalLong long getAsLong()
OptionalDouble double getAsDouble()
OptionalInt opt = OptionalInt.of(0); // OptionalInt에 0을 저장
OptionalInt opt2 = OptionalInt.empty(); // OptionalInt에 0을 저장
반환타입이 void이므로 스트림의 요소를 출력하는 용도로 많이 사용된다.
void forEach(Consumer<? super T> action)
boolean allMatch (Predicate<? super T> predicate) // 모든 요소가 일치하면 참
boolean anyMatch (Predicate<? super T> predicate) // 하나의 요소라도 일치하면 참
boolean noneMatch (Predicate<? super T> predicate) // 모든 요소가 불일치하면 참
// 총점이 100이하인 학생이 있는지 확인
boolean noFailed = stuStream.anyMatch(s->s.getTotalScore()<=100)
//filter()와 함께 첫 번째 것을 반환하는 findFirst()사용
Optional<Student> stu = stuStream.filter(s->s.getTotalScore()<= 100).findFirst();
// 병렬 스트림인 경우에는 findFirst()대신 findAny()
Optional<Student> stu = parallelStream.filter(s->s.getTotalScore()<=100).findAny();
요소를 하나씩 소모해버리기
Optional<T> reduce(BinaryOperator<T> accumulator)
int count = intStream.reduce(0, (a, b) -> a + 1);
int sum = intStream.reduce(0, (a, b) -> a + b);
int max = intStream.reduce(Integer.MIN_VALUE, (a, b)-> a>b ? a:b);
int min = intStream.reduce(Integer.MAX_VALUE, (a, b)-> a<b ? a:b);
최종 연산 중에서 가장 복잡하면서도 유용하게 활용될 수 있는 것이 collect(). collect()는 스트림의 요소를 수집하는 최종 연산으로 reduce()과 유사하다. collect()가 스트림의 요소를 수집하려면, 어떻게 수집할 것인가에 대한 방법이 정의되어있어야 하는데, 이 방법을 정의한 것인 바로 Collector이다.
collect() 스트림의 최종연산, 매개변수로 컬렉터를 필요로 한다.
Collector 인터페이스, 컬렉터는 이 인터페이스를 구현해야한다.
Collectors 클래스, static메서드로 미리 작성된 컬렉터를 제공한다.
Object collect(Collector collector)
잘 사용되지 않는데요.
스트림의 모든 요소를 컬렉션에 수집하려면, Collectors클래스의 toList()와 같은 메서드를 사용하면 된다. List나 Set이 아닌 특정 컬렉션을 지정하려면, toCollection()에 원하는 컬렉션의 생성자 참조를 매겨변수로 넣어주면 된다.
List<String> names = stuStream.map(Student::getName)
ArrayList<String> list = names.stream()
.collect(Collectors.toCollection(ArrayList::new));
Map<String, Person> map = persionStream
.collect(Collectors.toMap(p->p.getRegId(), p->p));
이쯤되니 정신이 나가버려엇 통계를 collect()로 얻을수도 있고 다른방법도 있는데 둘다 보여줌.
long count = stuStream.count();
long count = stuStream.collect(counting()); // Collectors.counting()
long totalScore = stuStream.mapToInt(Student::getTotalScore).sum();
long totalScore = stuStream.collect(summingInt(Student::getTotalScore));
Optional<Student> topStudent = stuStream
.max(Comparator.comparingInt(Student::getTotalScore));
Optional<Student> topStudent = stuStream
.collect(maxBy(comparator.comparingInt(Student::getTotalScore)));
IntSummaryStatistics stat = stuStream
.mapToInt(Student::getTotalScore).summaryStatistics();
IntSummaryStatistics stat = stuStream
.collect(summarizingInt(Student::getTotalScore));
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));
스트림의 요소가 문자열이 아닌 경우에는 먼저 map()을 이용해서 스트림의 요소를 문자열로 변환해야 한다.
String studentNames = stuStream.map(Student::getName).collect(joining());
String studentNames = stuStream.map(Student::getName).collect(joining(","));
String studentNames = stuStream.map(Student::getName).collect(
joining(",", "[", "]"));
collect()의 유용함이 여기서 나타난다고 하네요 어디 두고봅시다.
// 1. 기본 분할
Map<Boolean, List<Student>> stuBySex = stuStream
.collect(partitioningBy(Student::isMale)); // 학생들을 성별로 분할
List<Student> maleStudent = stuBySex.get(true); // Map에서 남학생 목록을 얻는다.
List<Student> femaleStudent = stuBySex.get(false); // Map에서 여학생 목록을 얻는다.
// 2. 기본 분할 + 통계 정보
Map<Boolean, Long> stuNumBySex = stuStream
.collect(partitioningBy(Student::isMale, counting()));
System.out.println("남학생 수 :" + stuNumBySex.get(true)); // 남학생 수 :8
System.out.println("여학생 수 :" + stuNumBySex.get(false)); // 여학생 수 :10
--- 남학생1등 여학생1등 구하기 ---
Map<Boolean, Optional<Student>> topScoreBySex = stuStream
.collect(
partitioningBy(Student::isMale,
maxBy(comparingInt(Student::getScore))
)
);
System.out.println("남학생1등 :"+ topScoreBySex.get(true));
System.out.println("여학생1등 :"+ topScoreBySex.get(false));
// 남학생 1등 :Optional[[나자바, 남, 1, 1, 300]]
// 여학생 1등 :Optional[[김지미, 여, 1, 1, 250]]
--- 만약에 150점 아래를 불합격처리하고싶다 ---
Map<Boolean, Optional<Student>> topScoreBySex = stuStream
.collect(
partitioningBy(Student::isMale,
partitioningBy(s -> s.getScore() < 150)
)
);
List<Student> faileMaleStu = failedStuBySex.get(true).get(true);
List<Student> faileFemaleStu = failedStuBySex.get(false).get(true);
Map<Integer, List<Student>> stuByBan = stuStream
.collect(groupingBy(student::getBan, toList())); // toList()가 생략가능
Map<Integer, HashSet<Student>> stuByBan = stuStream
.collect(groupingBy(student::getBan, toCollection(HashSet::new)));
--- HIGH, MID, LOW 분류하기 ---
Map<Student.Level, Long> stuByLevel = stuStream
.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())
); // [MID] - 8명, [HIGH] - 8명, [LOW] - 2명
--- groupingBy()를 여러번 쓰면 다수준 그룹화 가능 ---
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan = stuStream
.collect(groupingBy(Student::getHak, // 학년별 그룹화
groupingBy(Student::getBan) // 반별 그룹화
));
--- 각 반의 1등을 출력하고 싶다면 ---
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan = stuStream
.collect(groupingBy(Student::getHak,
groupingBy(Student::getBan,
collectingAndThen(
maxBy(comparingInt(Student::getScore)),
Optional::get
)
)
));
--- 각 학년별과 반별로 그룹화한 후 에 성적그룹을 Set에 저장 ---
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan = stuStream
.collect(groupingBy(Student::getHak,
groupingBy(Student::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())
)
)
);