ch14-15,16 스트림, 스트림의 특징
스트림
- 다양한 데이터 소스(컬렉션, 배열)를 표준화된 방법으로 다루기 위한 것
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> intStream = list.stream();
Stream<String> strStream = Stream.of(new String[]{"a", "b", "c"}); // 배열
Stream<Integer> evenStream = Stream.iterate(0, n -> n + 2); // 0,2,4,6 ...
Stream<Double> randomStream = Stream.generate(Math::random); // 람다식
IntStream intStream = new Random().ints(5); // 난수 스트림(크기가 5)
- 스트림이 제공하는 기능 - 중간 연산과 최종 연산
중간 연산 : 연산결과가 스트림인 연산. 반복적으로 적용가능
최종 연산 : 연산결과가 스트림이 아닌 연산. 단 한번만 적용가능(스트림의 요소를 소모)
stream.distinct().limit(5).sorted().forEach(System.out::println)
<----------중간연산----------><----------최종연산---------->
스트림의 특징
- 스트림은 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않는다.
List<Integer> list = Arrays.asList(3, 1, 5, 4, 2);
// list를 정렬해서 새로운 List에 저장
List<Integer> sortedList = list.stream().sorted().collect(Collectors.toList());
System.out.println("list : " + list); // list : [3, 1, 5, 4, 2]
System.out.println("sortedList : " + sortedList); // sortedList : [1, 2, 3, 4, 5]
- 스트림은 Iterator처럼 일회용이다.(필요하면 다시 스트림을 생성해야함)
strStream.forEach(System.out::println); // 모든 요소를 화면에 출력(최종연산)
int numOfStr = strStream.count(); // 에러. 스트림이 이미 닫혔음
- 최종 연산 전까지 중간연산이 수행되지 않는다. - 지연된 연산
IntStream intStream = new Random().ints(1, 46);
intStream.distinct().limit(6).sorted()
.forEach(i -> System.out.println(i + ","));
- 스트림은 작업을 내부 반복으로 처리
stream.forEach(System.out::println);
- 스트림은 작업을 병렬로 처리 - 병렬스트림(parallel())
Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
int sum = strStream.parallel().mapToInt(s -> s.length()).sum();
System.out.println("sum = " + sum);
- 기본형 스트림 - IntStream, LongStream, DoubleStream
오토박싱&언박신의 비효율이 제거됨(Stream<Integer> 대신 IntStream사용)
숫자와 관련된 유용한 메서드를 Stream<T>보다 더 많이 제공
ch14-17~22 스트림만들기
스트림 만들기 - 컬렉션
-Collection 인터페이스의 stream()으로 컬렉션을 스트림으로 변환
Stream<E> stream() // Collection 인터페이스의 메서드
스트림 만들기 - 배열
- 객체 배열로부터 스트림 생성하기
Stream<T> Stream.of(T... values) // 가변 인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive)
Stream<String> strStream = Stream.of("a","b","c"); // 가변 인자
// 둘 중 편한거 사용
Stream<String> strStream = Stream.of(new String[]{"a","b","c"});
Stream<String> strStream = Arrays.stream(new String[]{"a","b","c"});
Stream<String> strStream = Arrays.stream(new String[]{"a", "b", "c"}, 0, 3);
IntStream IntStream.of(int.. values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)
스트림 만들기 - 임의의 수
- 난수를 요소로 갖는 스트림 생성하기
IntStream intStream = new Random().ints(); // 무한 스트림
intStream.limit(5).forEach(System.out::println); // 5개의 요소만 출력
IntStream intStream = new Random().ints(5); // 크기가 5인 난수 스트림을 반환
ints(5, 10) 5<= x <10
스트림 만들기 - 특정 범위의 정수
- 특정 범위릐 정수를 요소로 갖는 스트림 생성하기(IntStream, LongStream)
IntStream intStream = IntStream.range(1, 5); // 1,2,3,4
IntStream intStream2 = IntStream.rangeClosed(1, 5); // 1,2,3,4,5
스트림 만들기 - 람다식 iterate(), generate()
- 람다식을 소스로 하는 스트림 생성하기
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) // 이전 요소에 종속적
static <T> Stream<T> generate(Supplier<T> s) // 이전 요소에 독립적
- iterate()는 이전 요손를 seed로 해서 다음 요소를 계산
Stream<Integer> evenStream = Stream.iterate(0, n -> n+2); // 0, 2, 4, 6 ...
evenStream.limit(10).forEach(System.out::println); // 0,2,4,6,8,10,12,14,16,18
- generate()는 seed 사용X
Stream<Double> randomStream = Stream.generate(Math::random);
Stream<Integer> oneStream = Stream.generate(() -> 1); // 1,1,1,1 ...
스트림 만들기 - 파일과 빈 스트림
- 파일 소스로 하는 스트림 생성하기
Stream<Path> Files.list(Path dir) // Path는 파일 또는 디렉토리
Stream<String> Files.lines(Path path)
Stream<String> Files.lines(Path path, Charset cs)
Stream<String> lines() // BufferedReader클래스의 메서드
- 비어있는 스트림 생성하기
Stream emptyStream = Stream.empty();
long count = emptyStream.count(); // count = 0
ch14-23~25 스트림의 연산
스트림의 연산
- 스트림이 제공하는 기능 - 중간 연산과 최종 연산
중간 연산 : 연산결과가 스트림인 연산. 반복적으로 적용가능
최종 연산 : 연산결과가 스트림이 아닌 연산. 단 한번만 적용가능(스트림의 요소를 소모)
stream.distinct().limit(5).sorted().forEach(System.out::println)
<----------중간연산----------><----------최종연산---------->
ch14-26~29 스트림의 중간연산(1)
스트림의 중간연산
- 스트림 자르기 - skip(), limit()
Stream<T> skip(long n) // 앞에서부터 n개 건너뛰기
Stream<T> limit(long maxSize) // maxSize 이후의 요소는 잘라냄
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.skip(3).limit(5).forEach(System.out::println); // 45678
- 스트림의 요소 걸러내기 - filter(), distinct()
Stream<T> filter(Predicate<? super T> predicate) // 조건에 맞지 않은 요소 제거
Stream<T> distinct() // 중복제거
IntStream intStream = IntStream.of(1,2,2,3,3,3,4,5,6,5);
intStream.distinct().forEach(System.out::println); // 123456
IntStream intStream = IntStream.rangeClosed(1,10); // 12345678910
intStream.filter(i -> i % 2 == 0).forEach(System.out::println); // 246810
intStream = IntStream.rangeClosed(1,10); // 12345678910
intStream.filter(i -> i % 2 != 0 && i % 3 != 0).forEach(System.out::println); // 157
intStream = IntStream.rangeClosed(1,10); // 12345678910
intStream.filter(i -> i % 2 != 0).filter(i -> i%3 != 0).forEach(System.out::println); // 157
- 스트림 정렬하기 - sorted()
Stream<T> sorted() // 스트림 요소의 기본 정렬(Comparale)로 정렬
Stream<T> sorted(Comparator<? super T> comparator) // 지정된 Comparator로 정렬
- Comparator의 comparing()으로 정렬 기준 제공
comparing(Function<T, U> keyExtractor)
comparing(Function<T, U> keyExtrator, Comparator<U> keyComparator)
studentStream.sorted(Comparator.comparing(Student::getBan) // 반별로 정렬
.forEach(System.out::println);
Student::getBan ===> (students) -> s.getBan()
- 추가 정렬 기준을 제공할 때는 thenComparing()을 사용(정렬 기준 여러개)
thenComparing(Comparator<T> other)
thenComparing(Function<T, U> keyExtractor)
thenComparing(Function<T, U> keyExtractor, Comparator<U> keyComp)
studentStream.sorted(Comparator.comparing(Student::getBan)
.thenComparing(Student::getTotalScore)
.thenComparing(Student::getName))
.forEach(System.out::println);
ex)
class Student implements Comparable<Student> {
String name;
int ban;
int totalScore;
Student(String name, int ban, int totalScore) {
this.name = name;
this.ban = ban;
this.totalScore = totalScore;
}
public String toString() {
return String.format("[%s, %d, %d]", name, ban, totalScore);
}
String getName() {
return name;}
int getBan() {
return ban;
}
int getTotalScore() {
return totalScore;
}
@Override
public int compareTo(Student s) {
return s.totalScore - this.totalScore;
}
}
public class Test {
public static void main(String[] args) {
Stream<Student> studentStream = Stream.of(
new Student("이자바", 3, 300),
new Student("김자바", 1, 200),
new Student("안자바", 2, 100),
new Student("박자바", 2, 150),
new Student("소자바", 1, 200),
new Student("나자바", 3, 290),
new Student("감자바", 3, 180)
);
// studentStream.sorted(Comparator.comparing(Student::getBan) // 1. 반별 정렬
studentStream.sorted(Comparator.comparing((Student s) -> s.getBan()) // 1. 반별 정렬
.thenComparing(Comparator.naturalOrder())) // 2. 기본 정렬
.forEach(System.out::println);
}
}
결과
[김자바, 1, 200]
[소자바, 1, 200]
[박자바, 2, 150]
[안자바, 2, 100]
[이자바, 3, 300]
[나자바, 3, 290]
[감자바, 3, 180]
- 스트림 요소 변환하기 - map()
Stream<R> map(Function<? super T, ? extends R> mapper) // Stream<T> -> Stream<R>
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1"),
new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
Stream<String> filenameStream = fileStream.map(File::getName);
filenameStream.forEach(System.out::println);
fileStream = Stream.of(new File("Ex1.java"), new File("Ex1"),
new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
fileStream.map(File::getName) // Stream<File> -> Stream<String>
.filter(s -> s.indexOf('.') != -1) // 확장자가 없는 것은 제외
.map(s -> s.substring(s.indexOf('.') + 1)) // Stream<String> -> Stream<String> / 파일 이름 전체 -> 확장자
.map(String::toUpperCase) // Stream<String> -> Stream<String>
.distinct() // 중복 제거
.forEach(System.out::println); // JAVA BAK TXT
- 스트림의 요소를 소비하지 않고 엿보기 - peek()
Stream<T> peek(Consumer<? super T> action) // 중간 연산(스트림 요소 소비X)
void forEach(Consumer<? super T> action) // 최종 연산(스트림을 소비)
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1"),
new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
fileStream.map(File::getName) // Stream<File> -> Stream<String>
.filter(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); // 최종 연산 스트림 소비
결과
filename=Ex1.java
extension=java
java
filename=Ex1.bak
extension=bak
bak
filename=Ex2.java
extension=java
java
filename=Ex1.txt
extension=txt
txt
- 스트림의 스트림을 스트림으로 변환 - flatMap()
Stream<String[]> strArrStrm = Stream.of(new String[]{"abc", "def", "ghi"},
new String[]{"ABC", "GHI", "JKL"});
// Stream<Stream<String>> strStrStrm = strArrStrm.map(Arrays::stream);
// strStrStrm.forEach(System.out::println);
Stream<String> strStrm = strArrStrm.flatMap(Arrays::stream);
strStrm.map(String::toLowerCase) // 소문자로 변경
.distinct() // 중복제거
.sorted() // 정렬
.forEach(System.out::println);
System.out.println();
String[] lineArr = {
"Believe or not It is true",
"Do or do not There is no try",
};
Stream<String> lineStream = Arrays.stream(lineArr);
lineStream.flatMap(line -> Stream.of(line.split(" +"))) // " +" : 하나 이상의 공백(정규식)
.map(String::toLowerCase)
.distinct()
.sorted()
.forEach(System.out::println);
System.out.println();
Optional<T>
- T타입 객체의 래퍼클래스(Integer, Long) - Optional<T>
public final class Optional<T> {
private final T value; // T타입의 참조변수(모든 타입의 객체 저장 가능)
...
}
간접적으로 null 다룬다.
Optional 사용 이유
ex)
Object result = getResult();
반환값은 null 또는 객체 이다.
1) result.toString() // NullpointException 발생
null을 직접 다루는 것은 위험(NullpointException 등)
2) if (result != null)
println(result.toString());
null 체크 -> if문 필수 -> 코드 지져분
Optional을 사용하면 result는 항상 null이 아니다.
Optional 객체
result 0x100 --> null
- Optional<T> 객체를 생성하는 다양한 방법
String str = "abc";
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(null); // NullPointException 발생
Optional<String> optVal = Optional.ofNullable(null); // OK
- null 대신 빈 Optional<T> 객체를 사용하자
Optional<String> optVal = null; // 바람직하지 않음, null로 초기화
Optional<String> optVal = Optional.empty(); // 빈 객체로 초기화
// Optional.empty()를 Optional.<String>empty()로 해도 된다.
Optional<T> 객체의 값 가져오기
- Optional객체의 값 가져오기 - get(), orElse(), orElseGet(), orElseThrow()
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal 값 반환. mull이면 예외 발생
String str2 = optVal.orElse(""); // optVal값이 null일 때는 , "" 반환
// T orElseGet(Supplier<? extends T> supplier)
String str3 = optVal.orElseGet(String::new); // 람다식 사용가능 () -> new String()
// T orElseThrow(Supplier<? extends X> exceptionSupplier)
String str4 = optVal.orElseThrow(NullPointerException::new); // null이면 예외 발생
- isPresent() - Optional객체의 값이 null이면 false, 아니면 true 반환
if (Optional.ofNullable(str).isPresent()) { // if (str != null) {
System.out.println(str);
}
// ifPresent(Consumer) - 널이 아닐때만 작업 수행, 널이면 아무 일도 안 함
Optional.ofNullable(str).ifPresent(System.out::println);
OptionalInt, OptionalLong, OptionalDouble
- 기본형 값을 감싸는 래퍼클래스
public final class OptionalInt {
...
private final boolean isPresent; // 값이 저장되어 있으면 true
private final int value; // int 타입의 변수
- OptionalInt의 값 가져오기 - int getAsInt()
Optional 클래스 | 값을 반환하는 메서드 |
Optional<T> | T get() |
OptionalInt | int getAsInt() |
OptionalLong | long getAsLong() |
OptionalDouble | double getAsDouble() |
- 빈 Optional 객체와의 비교
OptionalInt opt = OptionalInt.of(0); // OptionalInt에 0 저장
OptionalInt opt2 = OptionalInt.empty(); // OptionalInt에 0 저장 (아무 값도 저장X)
System.out.println(opt.isPresent()); // true
System.out.println(opt2.isPresent()); // false
System.out.println(opt.equals(opt2)); // false
OptionalInt에 있는 boolean값으로 비교하여 알 수 있다.
ch14-40~44 스트림의 최종연산에 대한 강의입니다.
중간 연산 : n번 / Stream 반환 / limit(), skip(), filter(), distint(), map(), flatMap()
최종 연산 : 1번 / int, boolean, Optional 반환 / forEach(), allMatch(), anyMatch(), noneMatch() ...
스트림의 최종연산 - forEach()
- 스트림의 모든 요소에 지정된 작업을 수행 - forEach(), forEachOrdered()
void forEach(Consumer<? super T> action) // 병렬스트림인 경우 순서 보장X
void forEachOrdered(Consumer<? super T> action) // 병렬스트림의 경우에도 순서 보장
// 기본적으로 스트림은 직렬스트림이려서 sequential()은 생략해도 된다.
IntStream.range(1,10).sequential().forEach(System.out::print); // 123456789
IntStream.range(1,10).sequential().forEachOrdered(System.out::print); // 123456789
// forEach() 순서 보장X
IntStream.range(1,10).parallel().forEach(System.out::print); // 657129348
// forEachOrdered() 순서 보장
IntStream.range(1,10).parallel().forEachOrdered(System.out::print); // 123456789
스트림의 최종연산 - 조건 검사
- 조건 검사 - allMatch(), anyMatch(), noneMatch()
boolean allMtch(Predicate<? super T> Predicate) // 모든 요소가 조건을 만족시키면 true
boolean allMtch(Predicate<? super T> Predicate) // 한 요소라도 조건을 만족시키면 true
boolean allMtch(Predicate<? super T> Predicate) // 모든 요소가 조건을 만족시키지 않으면 true
// stuStream은 Stream<Student> 이다
boolean hasFailedStu = stuStream.anyMatch(s -> s.getTotalScore() <= 100); // 낙제자가 있는지?
- 조건에 일치하는 요소 찾기 - findFirst(), findAny()
Optional<T> findFirst() // 첫 번째 요소 반환, 순차스트림에 사용
Optional<T> findAny() // 아무거나 하나를 반환, 병렬 스트림에 사용
Optional<Strudent> result = stuStream.filter(s -> s.getTotalScore() <= 100).findFirst();
Optional<Strudent> result = parallelStream.filter(s -> s.getTotalScore() <= 100).findAny();
스트림의 최종연산 - reduce()
- 스트림의 요소를 하나씩 줄여가며 누적연산 수행 - reduce()
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(U identity, BinaryOperator<U, T, U> accumulator, BinaryOperator<U> combiner)
identity : 초기값
accumulator : 이전 연산결과와 스트림의 요소에 수행할 연산
combiner : 병렬처리된 결과를 합치는데 사용할 연산(병렬 스트림)
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);
reduce(0, (a, b) -> a + 1);
reduce(0, (a, b) -> a + b);
은 풀어서 작성해 보면 아래와 같은 의미다.
int a = identity;
for (int b : stream)
a = a + b;
ex)
String[] strArr = {
"Inheritance", "Java", "Lambda", "stream",
"OptionalDouble", "IntStream", "count", "sum"
};
Stream.of(strArr).forEach(System.out::println);
System.out.println("-------------------------");
boolean noEmptyStr = Stream.of(strArr).noneMatch(s -> s.length() == 0);
System.out.println("noEmptyStr = " + noEmptyStr); // true
Optional<String> sWord = Stream.of(strArr).parallel()
.filter(s -> s.charAt(0) == 's').findFirst();
System.out.println("sWord = " + sWord.get()); //stream 또는 sum
System.out.println("-------------------------");
// Stream<String>을 Stream<Integer>으로 변환. (s) -> s.length()
// Stream<Integer> intStream = Stream.of(strArr).map(String::length);
// Stream<String[]>을 IntStream으로 변환
// {11,4,6,6,14,9,5,3}
IntStream intStream1 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream2 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream3 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream4 = Stream.of(strArr).mapToInt(String::length);
int count = intStream1.reduce(0, (a, b) -> a + 1);
System.out.println("count = " + count); // 8
int sum = intStream2.reduce(0, (a, b) -> a + b);
System.out.println("sum = " + sum); // 58
OptionalInt max = intStream3.reduce(Integer::max);
System.out.println("max = " + max); // OptionalInt[14]
System.out.println("max = " + max.getAsInt()); // 14
OptionalInt min = intStream4.reduce(Integer::min);
System.out.println("min = " + min); // OptionalInt[3]
System.out.println("min = " + min.getAsInt()); // 3
ch14-45~49 collect()와 Collectors에 대한 강의입니다.
collect()와 Collectors
- collect()는 Collector를 매개변수로 하는 스트림의 최종연산
Object collect(Collector collector) // Collector를 구현한 클래스의 객체를 매개변수로
Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) // 잘 안쓰임
- Collector는 수집(collect)에 필요한 메서드를 정의해 놓은 인터페이스
public interface Collector<T, A, R> { // T(요소)를 A에 누적한 다음, 결과를 R로 변환해서 반환
Supplier<A> supplier(); // StringBuilder::new 누적할 곳
BiConsumer<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를 구현한 클래스)를 제공
변환 - mapping(), toList(), toSet(), toMap(), toCollection() ...
통계 - counting(), summingInt(), averagingInt(), maxBy(), minBy() ...
문자열 결합 - joining()
리듀싱 - reducing()
그룹화와 분할 - groupingBy(), partitioningBy(), collectingAndThen()
스트림을 컬렉션, 배열로 변환
- 스트림을 컬렉션으로 변환 - toList(), toSet(), toMap(), toCollection()
List<String> names = stuStream.map(Student::getName).collect(Collectors.toList());
ArrayList<String> list = names.stream().collect(Collectors.toCollection(ArrayList::new));
Map<String> map = personStream.collect(Collectors.toMap(p->p.getRegId(), p->p)); // p->p : p값을 그대로
- 스트림을 배열로 변환 - toArray()
Student[] stuNames = studentStream.toArray(Student[]::new); // OK / (i) -> new Student[i]
Student[] stuNames = studentStream.toArray(); // error!! / 자동 형변환X (toArray() 는 Object[] 반환)
Object[] stuNames = studentStream.toArray(); // OK
toArray() 반환값은 Object[]이며 자동 형변환이 안되어 두 번째 코드는 에러 발생
- 스트림의 통계정보 제공 - counting(), summingInt(), maxBy(), minBy(), ...
long count = stuStream.count(); // 전체 수
long count = stuStream.collect(counting()); // Collectors.counting() / 그룹별 counting 가능
long totalScore = stuStream.mapToInt(Student::getTotalScore).sum(); // IntStream의 sum() / 전체 총점
long totalScore = stuStream.collect(summingInt(Student::getTotalScore)); // 그룹별 합계 가능
OptionalInt topScore = studentStream.mapToInt(Student::getTotalScore).max();
Optional<Student> topStudent = stuStream.max(Comparator.comparingInt(Student::getTotalScore));
Optional<Student> topStudent = stuStream.collect(maxBy(Comparator.comparingInt(Student::getTotalScore)));
비교기준
스트림을 리듀싱 - reducing() 그룹별 / reduce() 전체
- 스트림을 리듀싱 - reducing()
Collector reducing(BinaryOperator<T> op)
Collector reducing(T identity, BinaryOperator<T> op)
Collector reducing(U identity, Function<T, U> mapper, BinaryOperator<T> op) // map+reduce
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::getToTotalScore).reduce(0, Integer::sum);
int grandTotal = stuStream.collect(reducing(0, Student::getToTotalScore, Integer::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(",", "[","]"));
String studentInfo = stuStream.collect(joining(",")); // Student의 toString()으로 결합
ch14-50~55 스트림의 그룹화와 분할에 대한 강의입니다.
스트림의 그룹화와 분할
- partitioningBy()는 스트림을 2분할한다.
- groupingBy()는 스트림을 n분할한다.
스트림의 분할 - partitioningBy()
- 스트림의 요소를 2분할
Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downstream)
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)
};
System.out.printf("1. 단순분할(성별로 분할)%n");
Map<Boolean, List<Student>> stuBySex = Stream.of(stuArr)
.collect(partitioningBy(Student::isMale)); // 학생등을 성별로 분할
List<Student> maleStudent = stuBySex.get(true); // Map에서 남학생 목록을 얻는다.
List<Student> femaleStudent = stuBySex.get(false); // Map에서 여학생 목록을 얻는다.
for(Student s : maleStudent) System.out.println(s);
for(Student s : femaleStudent) System.out.println(s);
System.out.printf("%n2. 단순분할 + 통계(성별 학생수)%n");
Map<Boolean, Long> stuNumBySex = Stream.of(stuArr)
.collect(partitioningBy(Student::isMale, counting())); // 분할 + 통계
System.out.println("남학생 수 :"+ stuNumBySex.get(true)); // 남학생 수 : 8
System.out.println("여학생 수 :"+ stuNumBySex.get(false)); // 여학생 수 :10
System.out.printf("%n3. 단순분할 + 통계(성별 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)); // 남학생 1등 :Optional[[나자바, 남, 1학년 1반, 300점]]
System.out.println("여학생 1등 :"+ topScoreBySex.get(false)); // 여학생 1등 :Optional[[김지미, 여, 1학년 1반, 250점]]
Map<Boolean, Student> topScoreBySex2 = Stream.of(stuArr)
.collect(partitioningBy(Student::isMale,
collectingAndThen(
maxBy(comparingInt(Student::getScore)), Optional::get
)
));
System.out.println("남학생 1등 :"+ topScoreBySex2.get(true)); // 남학생 1등 :[나자바, 남, 1학년 1반, 300점]
System.out.println("여학생 1등 :"+ topScoreBySex2.get(false)); // 여학생 1등 :[김지미, 여, 1학년 1반, 250점]
System.out.printf("%n4. 다중분할(성별 불합격자, 100점 이하)%n");
Map<Boolean, Map<Boolean, List<Student>>> failedStuBySex =
Stream.of(stuArr).collect(partitioningBy(Student::isMale,
partitioningBy(s -> s.getScore() <= 100))
);
List<Student> failedMaleStu = failedStuBySex.get(true).get(true);
List<Student> failedFemaleStu = failedStuBySex.get(false).get(true);
for(Student s : failedMaleStu) System.out.println(s);
/*
[남자바, 남, 1학년 2반, 100점]
[남자바, 남, 2학년 2반, 100점]
*/
for(Student s : failedFemaleStu) System.out.println(s);
/*
[안지미, 여, 1학년 2반, 50점]
[황지미, 여, 1학년 3반, 100점]
[안지미, 여, 2학년 2반, 50점]
[황지미, 여, 2학년 3반, 100점]
*/
Key | Value | |
true | List<Student> | 남학생 |
false | List<Student> | 여학생 |
스트림의 그룹화 - groupingBy()
- 스트림의 요소를 그룹화
Map<Integer, List<Student>> stuByBan = Stream.of(stuArr)
.collect(groupingBy(Student::getBan)); // 학생을 반별로 그룹화
K | V |
1 | List<Student> |
2 | List<Student> |
3 | List<Student> |
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan =
Stream.of(stuArr)
.collect(groupingBy(Student::getHak, // 1. 학년별 그룹화
groupingBy(Student::getBan) // 2. 반별 그룹화
));
Map<String, Set<Student.Level>> stuByScoreGroup = Stream.of(stuArr)
.collect(groupingBy(s-> s.getHak() + "-" + s.getBan(), // 다중 그룹화(학년별, 반별)
mapping(s-> { // 성적등급(Level)으로 변환. List<Student> -> Set<Student, Level>
if(s.getScore() >= 200) return Student.Level.HIGH;
else if(s.getScore() >= 100) return Student.Level.MID;
else return Student.Level.LOW;
} , toSet()) // mapping()
));
ex)
package test;
import java.io.File;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.*;
class Student {
String name;
boolean isMale; // 성별
int hak; // 학년
int ban; // 반
int score;
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;
}
String getName() { return name;}
boolean isMale() { return isMale;}
int getHak() { return hak;}
int getBan() { return ban;}
int getScore() { return score;}
public String toString() {
return String.format("[%s, %s, %d학년 %d반, %3d점]", name, isMale ? "남":"여", hak, ban, score);
}
enum Level {
HIGH, MID, LOW
}
}
public class Test {
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)
};
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);
}
}
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;
}));
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();
}
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);
}
}
*/
System.out.printf("%n4. 다중그룹화(학년별, 반별)%n");
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan =
Stream.of(stuArr)
.collect(groupingBy(Student::getHak, // 1. 학년별 그룹화
groupingBy(Student::getBan) // 2. 반별 그룹화
));
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);
}
}
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);
System.out.printf("%n6. 다중그룹화 + 통계(학년별, 반별 성적그룹)%n");
Map<String, Set<Student.Level>> stuByScoreGroup = Stream.of(stuArr)
.collect(groupingBy(s-> s.getHak() + "-" + s.getBan(), // 다중 그룹화(학년별, 반별)
mapping(s-> { // 성적등급(Level)으로 변환. List<Student> -> Set<Student, Level>
if(s.getScore() >= 200) return Student.Level.HIGH;
else if(s.getScore() >= 100) return Student.Level.MID;
else return Student.Level.LOW;
} , toSet()) // mapping()
));
Set<String> keySet2 = stuByScoreGroup.keySet();
for(String key : keySet2) {
System.out.println("["+key+"]" + stuByScoreGroup.get(key));
}
}
}
스트림의 변환
참고
https://www.youtube.com/watch?v=7Kyf4mMjbTQ&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=163&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=AOw4cCVUJC4&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=164&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=iY8ta9upajE&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=165&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=G2lPQB42GL8&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=166&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=sEa4RQGG0HU&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=167&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=W_kPjiTF9RI&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=168&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=M_4a4tUCSPU&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=169&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=u9KOajCP3D8&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=170&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
https://www.youtube.com/watch?v=VUh_t_j9qjE&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=171&ab_channel=%EB%82%A8%EA%B6%81%EC%84%B1%EC%9D%98%EC%A0%95%EC%84%9D%EC%BD%94%EB%94%A9
'개발 관련 강의 정리 > 자바의정석' 카테고리의 다른 글
[자바의 정석 - 기초편] 람다 정리 (0) | 2023.04.20 |
---|---|
[자바의정석] 애너테이션 정리 (0) | 2023.04.19 |
댓글