14장
1. 람다
람다식(Lambda Expression): 메서드의 이름과 반환타입을 제거하고 하나의 식으로 표현한 것
익명 함수(이름과 반환값이 없는 메서드)처럼 보이나 실제로는 익명 클래스의 객체
람다식은 메서드의 매개변수나 결과로 반환되므로 메서드를 매개변수처럼 사용 가능
/*
==========================================================================
1. 람다식의 예시
==========================================================================
*/
// 1.1. 메서드
int max(int a, int b) {
return a > b ? a : b;
}
// 1.1. 람다식
(int a, int b) -> { return a > b ? a : b; } // 1
(int a, int b) -> a > b ? a : b; // 2
(a, b) -> a > b ? a : b; // 3
// 1.2. 메서드
void printVar(String name, int i) {
System.out.println(name + "=" + i);
}
// 1.2. 람다식
(String name, int i) -> { System.out.println(name + "=" + i); } // 1
(name, i) -> { System.out.println(name + "=" + i); } // 2
(name, i) -> System.out.println(name + "=" + i) // 3
// 1.3. 메서드
int square(int x) {
return x * x;
}
// 1.3. 람다식
(int x) -> x * x // 1
(x) -> x * x // 2
x -> x * x // 3
// 1.4. 메서드
int roll() {
return (int)(Math.random()*6);
}
// 1.4. 람다식
() -> {
return (int)(Math.random()*6);
}
() -> return (int)(Math.random()*6)
//1.5. 메서드
int sumArr(int[] arr) {
int sum = 0;
for (int i : arr) {
sum += i;
}
return sum;
}
// 1.5. 람다식
(int[] arr) -> {
int sum = 0;
for(innt i : arr) {
sum += i;
}
return sum;
}
/*
==========================================================================
2. 메서드 참조
- 클래스이름::메서드이름
- 참조변수::메서드 이름
==========================================================================
*/
// 2.1. 람다식
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
// 2.1. 메서드 참조(컴파일러는 생략된 부분을 제네릭으로 알아냄)
Function<String, Integer> f = Integer::parseInt;
// 2.2. 람다식
Function<Integer, MyClass> f = (i) -> new MyClass(i);
// 2.2. 메서드 참조(생성자)
Function<Integer, MyClass> f = MyClass:new;
2. 스트림
2.1. 스트림의 소개
JDK 1.2부터 컬렉션 프레임워크가 도입되었다. 컬렉션 프레임워크는 많은 장점이 있었지만, 몇 가지 단점도 존재했다. 컬렉션이나 배열에 데이터를 담고 원하는 결과를 얻는 것은 가독성과 재사용성이 떨어진다. 또한 컬렉션 클래스에는 같은 기능의 메서드들이 중복으로 정의되어 컬렉션마다 각각 알맞은 메서드를 호출해야 했다. 이러한 문제점을 해결하기 위해 스트림(Stream)이 도입되었다. 스트림은 데이터 소스를 추상화하고 데이터를 다루는데 사용되는 메서드들을 정의했다. 스트림을 사용하면 데이터 소스와 관계없이 같은 방식으로 다룰 수 있으며 코드의 재사용성도 높일 수 있다. 여기다 더해서 스트림은 파일에 저장된 데이터도 동일하게 처리할 수 있다.
2.2. 특징
데이터 소스를 변경하지 않음: 필요하다면 정렬된 결과를 컬렉션에 담아서 반환할 수 있음
일회용: 한 번 사용하면 닫혀서 다시 사용할 수 없으며 필요한 경우 다시 생성
작업을 내부 반복으로 처리: 반복문을 메서드 내부에 숨겨 코드의 가독성을 높임
지연된 연산: 스트림은 최종 연산을 수행하기 전까지 중간 연산을 수행하지 않음(중간 연산에서는 수행할 작업 지정)
병렬 스트림: 스트림은 내부적으로 자바에서 제공하는 fork&join 프레임워크를 이용해서 자동적으로 연산을 병렬로 수행
2.3. 스트림의 예시
/*
==========================================================================
1. 스트림 - 배열
==========================================================================
*/
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"});
/*
==========================================================================
2. 스트림 - 임의의 수
- 메서드가 반환하는 스트림의 크기가 정해지지 않은 경우 무한 스트림
==========================================================================
*/
Int Stream intStream = new Random().ints(); // 무한 스트림
intStream.limit(5).forEach(System.out::println); // 5개 요소만 출력
/*
==========================================================================
3. 스트림 - 특정 범위의 정수
==========================================================================
*/
// 문법
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.rangeClose(1, 5); // 1, 2, 3, 4, 5
/*
==========================================================================
4. 스트림 - 람다식 iterate(), generate()
==========================================================================
*/
// 문법
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
static <T> Stream<T> generate(Supplier<T> s)
// 예제
Stream<Integer> evenStream = Stream.iterate(0, n -> n + 2); // 0, 2, 4, 6, ...
Stream<Double> randomStream = Stream.generate(Math::random);
/*
==========================================================================
5. 스트림 - 파일과 빈 스트림
==========================================================================
*/
// 문법
Stream<Path> Files.list(Path dir)
// 예제
Stream<String> Files.lines(Path path)
// 빈 스트림 예제
Stream emptyStream = Stream.empty(); // empty()는 빈 스트림을 생성해서 반환
long count = emptyStream.count(); // count == 0
2.4. 스트림의 연산
중간 연산: 연산 결과가 스트림인 연산. 스트림에 연속해서 중간 연산 가능
최종 연산: 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 한 번만 가능
2.4.1. 중간 연산
Stream<T> distinct( )
중복을 제거
Stream<T> filter(Predicate<T> predicate)
조건에 안 맞는 요소 제외
Stream<T> limit(long maxSize)
스트림의 일부를 잘라냄
Stream<T> skip(long n)
스트림의 일부를 건너뜀
Stream<T> peek(Consumer<T> action)
스트림의 요소에 작업 수행
Stream<T> sorted( ) Stream<T> sorted(Comparator<T> comparator)
스트림의 요소를 정렬
Stream<R> DoubleStream IntStream LongStream Stream<R> DoubleStream IntStream LongStream
map(Function<T, R> mapper) mapToDouble(ToDoubleFunction<T> mapper) mapToInt(ToIntFunction<T> mapper) mapToLong(ToLongFunction<T> mapper) flatMap(Function<T, Stream,R>> mappeR) flatMapToDouble(Function<T, DoubleStream> m) flatMapToInt(Function<T, IntStream> m) flatMapToLong(Function<T, LongStream> m)
스트림의 요소를 반환
2.4.2. 최종 연산
void forEach(Consumer<? super T> action) void forEachOrdered(Consumer<? super T> action)
각 요소에 지정된 작업 수행
long count( )
스트림의 요소의 개수 반환
Optional<T> max(Comparator<? super T> comparator) Optional<T> min(Comparator<? super T> comparator)
스트림의 최대값/최소값 반환
Optional<T> findAny( ) Optional<T> findFirst( )
스트림의 요소 하나를 반환
boolean allMatch(Predicate<T> p) boolean anyMatch(Predicate<T> p) boolean nonMatch(Predicate<T> p)
주어진 조건을 모든 요소가 만족시키는지 여부를 확인
Object[ ] toArray( ) A[ ] to Array(IntFunction<A[ ] generator)
스트림의 모든 요소를 배열로 반환
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)
스트림의 요소를 하나씩 줄여가면서 계산 (reducing)
R collect(Collector<T, A, R> collector) R collect(Supplier<R> supplier, BiConsumer<R, T> accumulator, BiConsumer<R, R> combiner)
- 스트림의 요소를 수집 - 주로 요소를 그룹화 or 분할한 결과를 컬렉션에 담아 반환
참고 자료
자바의 정석 - 도우출판
Last updated