
- 함수(메소드)를 간단한 식(expression)으로 표현하는 방법
근본적으로 동일. 함수는 일반적 용어. 메소드는 객체지향개념 용어
- 클래스 안에 있으면 메소드, 클래스 밖에 존재하면 함수
1. 메소드의 이름과 반환타입을 제거하고 '->'를 블럭{}앞에 추가
int max(int a, int b){ (int a, int b) -> {
return a > b ? a : b; → return a > b ? a : b;
} }
2. 반환값이 있는 경우 식이나 값만 적고 return문 생략 가능(끝에 ;안붙임)
(int a, int b) -> {
return a > b ? a : b; → (int a, int b) -> a > b ? a : b
}
3. 매개변수의 타입이 추론 가능하면 생략가능(대부분의 경우 생략가능)
(int a, int b) -> a > b ? a : b → (a, b) -> a > b ? a : b
1. 매개변수가 하나인 경우 괄호()생략가능 (타입이 없을때만)
(a) -> a * a → (a) -> a * a // ok
(int a) -> a * a → (int a) -> a * a // 에러!!
2. 블럭 안의 문장이 하나뿐 일 때, 괄호{}생략가능 (끝에 ' ; ' 안붙임)
(int i) -> {
System.out.println(i); → (int i) -> System.out.println(i)
}
❗ 람다식은 익명함수가 아니라 익명 객체이다!!
new Object(){ //익명객체
int max(int a, int b) {
(a, b) -> a > b ? a : b <-> return a > b ? a : b;
}
}
단 하나의 추상메소드만 선언된 인터페이스
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 f = (a, b) -> a > b ? a : b; //람다식을 위한 참조변수
int value = f.max(3,5); // ok!!
//익명객체를 람다식으로 대체
List<String> list = Arrays.asList{"abc", "aaa", "bbb", "ddd", "aaa"};
Collections.sort(list, new Comparator<String>() {
public int compare(String s1, String s2) {
return s2.compareTo(s1);
}
});
//다음과 같이 쓸수 있음
interface Comparator<T> {
compare(T o1, T o2);
}
List<String> list = Arrays.asList{"abc", "aaa", "bbb", "ddd", "aaa"};
Collections.sort(list, (s1,s2) -> s2.compareTo(s1)); //간단하게 쓸 수 있다
interface MyFunction{
void myMethod();
}
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;
//위 두줄을 한줄로
return () -> {};
}
자주 사용되는 다양한 함수형 인터페이스 제공
//ex
Predicate<String> isEmptyStr = s -> s.length () ==0;
String s = "";
if(isEmptyStr.test(s)) //if(s.length() == 0)와 같음 test()가 람다식이름
System.out.println("This is an empty String.");
매개변수가 2개인 함수형 인터페이스
//매개변수가 2개인 것 까지는 패키지 안에 존재. 3개부턴 직접 만들어야함
//매개변수가 3개인 함수형 인터페이스만들기
interface TriFunction<T,U,V,R> {
R apply(T t, U u, V v);
}
매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t; // t타입이 t타입으로 반환
}
}
and(), or(), negate()로 두 Predicate를 하나로 결합(default 메소드)
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) = i>=100
Predicate<Integer> all = notP.and(q).or(r); // 100 <= i && i < 200 || i%2 == 0
Predicate<Integer> all2 = notP.and(q.or(r)); // 100 <= i && (i < 200 || i%2 == 0)
//사용
System.out.println(all.test(2)); //true (2%2 == 0)
System.out.println(all2.test(2)); //false
등가비교시에는 isEqual()사용
Predicate<String> p = Predicate.isEqual(str1);
Boolean res = p.test(str2); //str1.equals(str2) 와 같음
//두줄을 한줄로
boolean res = Predicate.isEqual(str1).test(str2);
함수형 인터페이스를 사용하는 컬렉션 프레임워크의 메소드
(와일드카드 생략)
//ex
list.forEach(i -> System.out.println(i + " ")); //list의 모든 요소 출력
list.removeIf(x -> x%2 == 0 || x%3 == 0); //list의 2 또는 3의 배수 제거
list.replaceAll(i -> i*10);// list의 모든 요소에 10을 곱합.
//map의 모든 요소를 (k, v)의 형식으로 출력
map.forEach((k,v) -> System.out.println("{" + k+","+v+ "},"));
하나의 메소드만 호출하는 람다식은 '메소드 참조'로 간단히 할 수 있다.
| 종류 | 람다 | 메소드 참조 |
|---|---|---|
| static메소드 참조 | (x) -> ClassName.method(x) | ClassName::method |
| 인스턴스메소드 참조 | (obj, x) -> obj.method(x) | ClassName::method |
| 특정 객체 인스턴스메소드 참조 | (x) -> obj.method(x) | obj::method(잘 안씀) |
Integer method(String s) {
return Integer.parseInt(s);
}
int res = obj.method("123");
//람다식
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> s = (i) -> new MyClass(i);
//메소드 참조
Function<Integer, MyClass> s = MyClass::new;
Function<Integer, int[]> f = x -> new int[x]; //람다식
Function<Integer, int[]> f2 = int[]::new; //메소드 참조
다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것
String[] strArr = { "dd", "aaa", "CC", "cc", "b" };
Stream<String> stream = Stream.of(strArr); //문자열 배열이 소스인 스트림
Collection인터페이스의 stream()으로 컬렉션을 스트림으로 변환
Stream<E> stream() //Collection인터페이스의 메소드
//예제
List<Integer> list = Arrays.asList{1, 2, 3, 4, 5};
Stream<Integer> intStream = list.stream(); //list를 스트림으로 변환
//스트림의 모든 요소 출력
intStream.forEach(System.out::print); //12345
intStream.forEach(System.out::print); //에러!! 스트림이 이미 닫힘
객체배열로 스트림 생성하기
Stream<T> Stream.of(T...values) //가변인자 Stream<T> Stream.of(T[]) Stream<T> Arrays.stream(T[]) Stream<T> Arrays.stream(T[] array, int from, int to) //from ~ (to-1)
//예제
Stream<T> Stream.of("a", "b", "c"); //가변인자
Stream<T> Stream.of(new String[]{"a", "b", "c"});
Stream<T> Arrays.stream(new String[]{"a", "b", "c"})
Stream<T> Arrays.stream(new String[]{"a", "b", "c"}, 0,3) //[0] ~ [2]
기본형 배열로 스트림 생성하기
IntStream IntStream.of(int... values) //Stream이 아닌 기본형스트림 IntStream IntStream.of(int[]) IntStream Arrays.stream(int[]) IntStream Arrays.stream(int[] array, int from, int to) //from ~ (to-1)
- 오토박싱&언박싱의 비효율이 제거(Stream<Integer> 대신 IntStream)
- 숫자와 관련된 유용한 메소드를 Stream<T>보다 더 많이 제공
- sum(), count(), average()...
난수를 요소로 갖는 스트림 생성하기
//예제
IntStream intStream = new Random().ints(); //무한 스트림
intStream.limit(5).forEach(System.out::println); //5개의 요소만 출력
IntStream intStream = new Random().ints(5); //크기가 5인 난수 스트림 반환
//Random클래스에 있는 ints(), longs(), doubles()
//무한 스트림을 생성함
지정된 범위의 난수를 요소로 갖는 스트림을 생성하는 메소드
//무한 스트림 IntStream ints(int begin, int end) LongStream longs(long begin, long end) DoubleStream doubles(double begin, double end) //유한스트림 IntStream ints(long streamSize, int begin, int end) LongStream longs(long streamSize, long begin, long end) DoubleStream doubles(long streamSize, double begin, double end)
특정 범위의 정수를 요소로 갖는 스트림 생성 (IntStream, LongStream)
IntStream IntStream.range(int begin, int end) IntStream IntStream.rangeClosed(int begin, int end) //end도 포함
//예제
IntStream IntStream.range(1, 5) // 1, 2, 3, 4
IntStream IntStream.rangeClosed(1, 5) // 1, 2, 3, 4, 5 (end도 포함)
람다식을 소스로 하는 스트림 생성
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) //이전 요소에 종속적 static <T> Stream<T> generate(Supplier<T> s) //이전 요소에 독립적
//예제
//iterate - 초기값이 0(seed)이고 2씩 더해지는 등차수열
Stream<Integer> evenStream = Stream.iterate(0, n -> n+2); //0, 2, 4, 6, ...
//generate - 1로만 이루어지는 무한 스트림
Stream<Integer> oneStream = Stream.generate(() -> 1); // 1, 1, 1, 1, 1, 1 ....
파일을 소스로 하는 스트림 생성
Stream<Path> Files.list(Path.dis) //Pathh는 파일 또는 디렉토리 //line단위 Stream<String> Files.lines(Path path) Stream<String> Files.lines(Path path, Charset cs) Stream<String> lines() // BufferedReader클레스의 메소드
Stream emptyStream = Stream.empty(); //empty()는 빈 스트림을 생성해 반환
long count = emptyStream.count(); //빈 스트림의 count값은 0
연산결과가 스트림인 연산. - 반복사용가능
Stream<T> skip(long n) //앞에서부터 n개 건너뛰기
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.skip(3).foreach(System.out::print); // 45678910
Stream<T> limit(long maxSize) //maxSize 이후의 요소는 잘라냄
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.limit(5).foreach(System.out::print); // 12345
Stream<T> filter(Predicate<? super T> predicate) //조건에 맞지 않는 요소 제거
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.filter(i -> i%2==0).foreach(System.out::print); // 246810
Stream<T> distinct() //중복 제거
IntStream intStream = IntStream.of(1, 2, 2, 3, 3, 3, 4, 5, 5, 6);
intStream.distinct().foreach(System.out::print); //123456
스트림 정렬하기
Stream<T> sorted() //스트림 요소의 기본 정렬(Comparable)로 정렬 Stream<T> sorted(Comparator<T> comparator) //지정된 Comparator로 정렬
Comparator의 comparing()으로 정렬 기준 제공
comparing(Function<T, U> keyExtractor)
comparing(Function<T, U> keyExtractor, Comparator<U> keyComparator)
studentStream.sorted(Comparator.comparing(Student::getBan)) //반별로 정렬
.forEach(System.out::println);
추가 정렬 기준을 제공할 때는 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);
스트림 요소 엿보기
Stream<T> peek(Consumer<? super T> action) //중간 연산(스트림의 요소를 소비x)
void forEach(Consumer<? super T> action) //최종 연산(스트림의 요소를 소비o)
fileStream.map(FileName::getName) //Stream<File> → Stream<String>
.filter(s -> s.indexOf('.')!=-1) //확장자가 없는 것은 제외
.peek(s->System.out.printf("filename=%s%n", s)) //파일명 출력-소비x
.map(s->s.substring(s.indexOf('.')+1)) //확장자만 추출
.peek(s->System.out.printf("extension=%s%n", s)) //확장자를 출력-소비x
.forEach(System.out::println); //최종연산 스트림을 소비
스트림 요소 변환
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); //스트림의 모든 파일의 이름을 출력
스트림의 스트림을 스트림으로 변환
Stream<String[]> strArrStrm = Stream.of(new String[]{"abc", "def", "ghi" },
new String[]{"ABC", "GHI", "JKL"})
Stream<Stream<String>> strStrStrm = strArrStrm.map(Arrays::stream); //스트림의 스트림
//스트림으로 변환 - 여러개의 문자열 배열을 하나의 문자열배열로 변환!
Stream<String> strStrStrm = strArrStrm.flatMap(Arrays::stream); // Arrays.stream(T[])
연산결과가 스트림이 아닌 연산. - 단 한번만 적용가능
T타입 객체의 래퍼클래스
public final class Optional<T>{ private final T value; //T타입의 참조변수 ... }
String str = "abc"; Optional<String> optval = Optional.of(str); Optional<String> optval = Optional.of("abc"); Optional<String> optval = Optional.of(null); //NullpointerException 발생 Optional<String> optval = Optional.ofNullable(null); // ok
null대신 빈 Optional<T> 객체를 사용권장
Optional<String> optVal = null; //널로 초기화하지 말고 Optional<String> optVal = Optional.empty(); //빈 객체로 초기화!
Optional객체 값 가져오기
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); //optVal에 저장된 값을 반환 null이면 예외발생
String str2 = optVal.orElse(""); //optVal에 저장된 값이 null일 때는, ""를 반환
String str3 = optVal.orElseGet(String::new); //람다식 사용가능 () -> new String()
String str4 = optVal.orElseThrow(NullPointerException::new); //null이면 예외발생
null값인지 알아내기 (null이 아니면 true, null이면 false)
ifPresent(Consumer) //null이 '아닐때'만 작업 수행. null이면 아무 일도 안함
Optional.ofNullable(str).ifPresent(System.out::println);
public final class OptionalInt { //OptionalInt, OptionalLong, OptionalDouble
...
private final boolean isPresent; //값이 저장되어 있으면 true
private final int value; //기본형타입의 변수
}
| Optional클래스 | 값을 반환하는 메소드 |
|---|---|
| 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저장
System.out.println(opt.isPresent()); //true
System.out.println(opt2.isPresent()); //false
System.out.println(opt.equals(opt2)); //false
모든 요소에 지정된 작업 수행
void forEach(Consumer<? super T> action) //병렬스트림인 경우 순서 보장x
void forEachOrdered(Consumer<? super T> action) //병렬스트림인 경우에도 순서 보장
//직렬로 처리
IntStream.range(1, 10).sequential().forEach(System.out::print); //123456789
IntStream.range(1, 10).sequential().forEachOrdered(System.out::print); //123456789
//병렬로 처리
IntStream.range(1, 10).parallel().forEach(System.out::print); //683295714 - 순서보장x
IntStream.range(1, 10).parallel().forEachOrdered(System.out::print); //123456789 - 순서보장o
조건 검사
boolean allMatch (Predicate<? super T> predicate) //모든 요소가 조건을 만족시키면 true
boolean anyMatch (Predicate<? super T> predicate) //하나라도 조건을 만족시키면 true
boolean noneMatch (Predicate<? super T> predicate) //모든 요소가 조건을 만족시키지 않으면 true
boolean allFailedStu = stuStream.allMatch(s -> s.getTotalScore() <= 100); //모든 학생이 낙제자인가
boolean hasFailedStu = stuStream.anyMatch(s -> s.getTotalScore() <= 100); //낙제자가 한명이라도 존재하는가
boolean noneFailedStu = stuStream.noneMatch(s -> s.getTotalScore() <= 100); //낙제자가 한명도 없는가
조건에 일치하는 요소 찾기
//null값을 반환할 수도 있어서 Optional객체 사용
Optional<T> findFirst() //첫 번째 요소 반환. 순차 스트림에 사용
Optional<T> findAny() //아무거나 하나 반환. 병렬 스트림에 사용
Optional<Student> result = stuStream.filter(s -> s.getTotalScore() <= 100).findFirst();
Optional<Student> result = parallelStream.filter(s -> s.getTotalScore() <= 100).findAny();
요소를 줄여가며 누적연산 수행하기
/*
identity - 초기값
accumulator - 이전 연산결과와 스트림의 요소에 수행할 연산
combiner - 병렬처리된 결과를 합치는데 사용할 연산 (병렬 스트림)
*/
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(U identity, BiFunction<U, T, U> accumulator, BinaryOperator<U> combiner)
int count = intStream.reduce(0, (a,b) -> a+1); //count()
int sum = intStream.reduce(0, (a,b) -> a+b); //sum()
int max = intStream.reduce(Integer.MIN_VALUE, (a,b) -> a > b ? a : b); //max()
int min = intStream.reduce(Integer.MAX_VALUE, (a,b) -> a < b ? a : b); //min()
String[] strArr = { "dd", "aaa", "CC", "cc", "b" };
Stream<String> stream = Stream.of(strArr); //문자열 배열이 소스인 스트림
Stream<String> filteredStream = stream.filter(); //걸러내기 (중간연산)
Stream<String> distinctedStream = stream.distinct(); //중복제거 (중간연산)
Stream<String> sortedStream = stream.sort(); //정렬 (중간연산)
Stream<String> limitedStream = stream.limit(5); //스트림 자르기 (중간연산)
int total = stream.count(); //요소 개수 세기 (최종연산)
collect()는 Collector를 매개변수로 하는 스트림의 최종연산
Object collect(Collector collector) //Collector를 구현한 클래스의 객체를 매개변수로
Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) //잘 안쓰임
Collector는 수집에 필요한 메소드를 정의해 놓은 인터페이스
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(), summarizingInt(), ...
- 문자열 결합 - joining()
- 리듀싱 - reducing()
- 그룹화와 분할 - groupingBy(), partitioningBy(), collectingAndThen()
스트림을 컬렉션으로 변환
- toList(), toSet(), toMap(), toCollection()
List<String> names = stuStream.map(Student::getName) //Stream<Student> → Stream<String>
.collect(Collectors.toList()); //Stream<String> → List<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(); //에러. 자동형변환이 안됨. (Student[])사용
Object[] stuNames = studentStream.toArray(); // ok
스트림의 통계정보 제공
//counting()
long count = stuStream.count(); //항상 전체를 카운팅
long count = stuStream.collect(counting()); //그룹별로 카운팅이 가능
//summingInt()
long totalScore = stuStream.mapToInt(Student::getTotalScore).sum(); // IntStream의 sum()
long totalScore = stuStream.collect(summingInt(student::getTotalScore));
//maxBy()
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))); //그룹별로 최대값 추출가능
스트림을 리듀싱 (Collectors클래스의 메소드)
Collector reducing(BinaryOperator<T> op)
Collector reducing(T identity, BinaryOperator<T> op)
Collector reducing(U identity, Function<T, U> mapper, BinaryOperator<T> op) //mapper로 변환후 리듀싱
//1~46까지의 수를 중복없이 무작위로 6개만 추출한 int스트림
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)); //그룹별 리듀싱 가능
문자열 스트림의 요소를 모두 연결
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()으로 결합
스트림을 2분할한다.
Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downstream)
//분할
Map<Boolean, List<Student>> stuBySex = stuStream
.collect(partitioningBy(Student::isMale)); //학생들을 성별로 분할
List<Student> maleStudent = stuBySex.get(true); //Map에서 남학생 목록을 얻음
List<Student> femaleStudent = stuBySex.get(false); //Map에서 여학생 목록을 얻음
//분할 + 통계
Map<Boolean, Long> stuNumBySex = stuStream
.collect(partitioningBy(Student::isMale), counting()); //학생들을 성별로 분할후 그룹별 카운팅
System.out.println("남학생 수 : " + stuNumBySex.get(true));
System.out.println("여학생 수 : " + stuNumBySex.get(false));
//다중분할
Map<Boolean, Map<Boolean, List<Student>>> failedStuBySex = stuStream
.collect(partitioningBy(Student::isMale), //성별로 분할
partitioningBy(s->s.getScore() < 150)); //성적으로 한번더 분할
List<Student> failedMaleStu = failedStuBySex.get(true).get(true); //불합격한 남학생
List<Student> failedFemaleStu = failedStuBySex.get(false).get(true); //불합격한 여학생
스트림을 n분할한다.
Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory,
Collector downstream)
Map<Integer, List<Student>> stuByBam = stuStream //학생을 반별로 그룹화
.collect(groupingBy(Student::getBan, toList())); //toList() 생략가능
//다중 그룹화
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan = stuStream //다중그룹화
.collect(groupingBy(Student::getHak, //학년별 그룹화
groupingBy(Student::getBan)) //반별 그룹화
);
- 스트림은 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않는다.
List<Integer> list = Arrays.asList{3, 1, 5, 4, 2};
List<Integer> sortedList = list.stream().sorted()
.collect(Collection.toList());
System.out.println(list); // 3, 1, 5, 4, 2 - 변경x
System.out.println(sortedList); // 1, 2, 3, 4, 5
- 스트림은 Iterator처럼 일회용이다.(필요하면 다시 스트림 생성)
strStream.forEach(System.out.println); //모든 요소 화면에 출력(최종연산)
int numOfStr = strStream.count(); //에러!! 스트림은 이미 닫힘
- 최종연산 전까지 중간연산이 수행되지 않는다. - 지연된 연산
IntStream intStream = new Random().ints(1,46); // 1 ~ 45범위의 무한 스트림
IntStream.distinct().limit(6).sorted() //중간연산 - 바로실행x(지연된 연산)
.forEach(i -> System.out.println(i + ",")); //최종연산
- 스트림은 작업을 내부 반복으로 처리한다.
for(String str : strList)
System.out.println(str); → stream.forEach(System.out::println);
//스트림은 다음과 같이 내부에 넣어서 처리
void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action); //매개변수의 null 체크
for(T t : src) //내부 반복(for문을 메소드 안으로 넣음)
action.accept(T);
}
- 스트림의 작업을 병렬로 처리 - 병렬스트림
Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
int sum = strStream.parallel() // 병렬스트림으로 전환
.mapToInt(s -> s.length()).sum(); //모든 문자열 길이 합