: 메서드를 하나의 식(expression)으로 표현한 것
(타입 매개변수) -> { 실행문; ... }
public int sum(int a, int b) {
return a + b;
}
//람다식 변환
(a, b) -> a + b;
함수형 인터페이스는 람다식을 다루기 위한 인터페이스로 하나의 추상 메서드만 정의되어 있어야한다. 단, static 메서드와 default 메서드의 개수에는 제약이 없다.
스트림이란?
:다양한 데이터 소스를 표준화 된 방법으로 다루기 위한 라이브러리이다. 스트림은 데이터 소스를 추상화하고, 데이터를 다루는 데 자주 사용되는 메서드들을 정의해 놓았다.
스트림을 이용하면, 배열이나 컬렉션 뿐만 아니라 파일에 저장된 데이터도 모두 같은 방식으로 다룰 수 있다.
String[] strArr = {"aaa", "ddd", "ccc"};
List<String> strList = Arrys.asList(strArr);
문자열 배열 'strArr'과 같은 내용의 문자열을 저장하는 리스트 'strList'가 있을 때, 각각의 데이터를 정렬하고 출력하는 방법
1.스트림x
Arrays.sort(strArr);
Collections.sort(strList);
for(String str : strArr)
System.out.println(str);
for(String str : strList)
System.out.println(str);
2.스트림 활용한 방법
Stream<String> strStreamArr = Arrays.stream(strArr);
Stream<String> strStreamList = strList.stream();
strStremaArr.sorted().forEach(System.out::println);
strStreamList.sorted().forEach(System.out::println);
스트림을 사용한 코드가 더 간결하고 이해하기 쉬우면서 재사용성이 높다.
스트림이 제공하는 다양한 연산을 이용해서 복잡한 작업들을 간단하게 처리할 수 있다.
- 중간연산: 연산결과가 스트림. 연속에서 수행 가능.
- 최종연산: 연산결과가 스트림이 아님. 스트림의 요소를 소모하기 때문에 단 한번만 가능.
대부분의 메서드는 타입이 비슷하고 지네릭 메서드로 정의하면 반환 타입이 달라도 문제가 되지 않는다. java.util.function 패키지에는 일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해놓았으며 매번 함수형 인터페이스를 정의하기 보다는 가능하면 이 패키지의 인터페이스를 활용한다.
메서드를 참조해서 매개변수의 정보 및 리턴 타입을 알아내어 람다식에서 불필요한 매개 변수를 제거하는 것이 목적. 람다식의 매개 변수는 메서드의 매개값을 전달하는 역할만 하기 때문에 메서드 참조를 이용하면 깔끔하게 처리 할 수 있따.
(a, b) -> Math.max(a,b);
Math::max
생성자의 메서드 참조
스트림의 특징
특징1. 스트림은 데이터 소스를 변경하지 않는다.
스트림은 데이터 소스로부터 데이터를 읽기만 할 뿐, 데이터소스를 변경하지 않는다.
정렬된 결과가 필요할 경우, collect를 활용해서 컬렉션이나 배열에 담아 return할 수 있다.
List<String> sortedList = strStreamList.sorted().collect(Collectors.toList());
특징2. 스트림은 일회용이다.
스트림은 한번 사용하면 닫혀서 다시 사용할 수 없다. 필요하다면 스트림을 다시 생성해야 한다.
특징3. 스트림은 작업을 내부 반복으로 처리한다.
내부 반복이란, 반복문을 메서드의 내부에 숨길 수 있다는 것을 의미한다. forEach()는 스트림에 정의 된 메서드 중의 하나로 매개변수에 대입 된 람다식을 데이터소스의 모든 요소에 적용한다. 즉, forEach()는 메서드 안에 for문을 넣어버린 것이다.
//수행할 작업을 매개변수로 받는다.
strStreamArr.sorted().forEach(System.out::println);
특징4. 지연된 연산
스트림 연산에서는 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다. 스트림에 대해 sort()나 distinct()같은 중간 연산을 호출해도 즉각적으로 수행되지 않는다는 것이다. 중간 연산을 호출하는 것은 단지 어떤 작업이 수행되어야 하는지를 지정해주는 것일 뿐이다. 최종 연산이 수행되어서야 스트림의 요소들이 중간연산을 거치고 최종연산에 소모된다.
특징5. 기본형 스트림
오토박싱, 언박싱으로 인한 비효율을 줄이기 위해 데이터 소스의 요소를 기본형으로 다루는 InStream, LongStream, DoubleStream이 제공된다.
일반적으로 Stream< Integer>대신 IntStream을 사용하는 것이 더 효율적이고, IntStream에는 int타입으로 작업하는데 유용한 메서드들이 포함되어 있다.
특징6. 병렬스트림
스트림은 내부적으로 framework를 이용해서 연산을 자동적으로 병렬로 수행한다.
parallel()메서드를 호출하면 병렬로 연산이 수행되고, sequential()메서드를 호출하면 병렬로 처리되지 않게된다. 모든 스트림은 기본적으로 병렬 스트림이 아니기 때문에 sequential()메서드는 parallel()를 취소할때만 사용한다.
int sum = strStream.parallel().mapToInt(s -> s.length()).sum();
Stream.of("3", "1", "4", "2", "5", "5") // 문자열 스트림 생성
.map(Integer::parseInt) // 문자열 스트림을 정수형 스트림으로 변환
.sorted() // 정렬
.distinct() // 중복제거
.limit(3) // 갯수를 3개로 제한
.collect(Collectors.toList()) // 리스트로 변환 => {1, 2, 3} : 단말 연산
.stream() // 다시 정수형 값을 갖는 스트림으로 변환
.filter(x -> x > 1) // 1보다 큰 값만 갖도록 필터링함 {2, 3}
.forEach(System.out::println); // 2와 3만 출력됨
자바 8 이후부터 추가된 stream은 for문을 사용한 반복문이 갖는 단점들을 해결해 줄 수 있다. for문이 갖는 단점으로는 쉽게 indent가 증가하고 if문 같은 분기문이 쉽게 발생한다는 점이다.
문법
stream().중개연산.단말연산
문법은 아래의 코드처럼 stream() 생성을 하고 중개 연산 결과들을 계속 반환하면서 이어가다 단말 연산을 이용해 멈춘다.
ArrayList<RacingCar> winners = new ArrayList<>();
for (RacingCar racingCar : racingCars) {
if (racingCar.isSamePosition(racingCarOfMaxPosition)) {
winners.add(racingCar);
}
}
return winners;
// stream api를 활용한 식
private final List<RacingCar> racingCars = new ArrayList<>();
return racingCars.stream()
.filter(racingCar -> racingCar.isSamePosition(racingCarOfMaxPosition))
.collect(Collectors.toCollection(ArrayList::new));
Stream 클래스의 iterate(), generate() 는 람다식을 매개변수로 받아서, 이 람다식에 의해 계산되는 결과값들을 요소로 하는 무한 스트림을 생성한다.
iterate(): 이전 결과에 대해 종속적
Stream<Integer> evenStream = Stream.iterate(0, n->n+2);
// 0, 2, 4, 6, ...
generate(): 이전 결과에 대해 독립적
Stream<Double> randomStream = Stream.generate(Math::random);
IntStream evenStream = Stream.iterate(0, n->n+2); //error
DoubleStream randomStream = Stream.generate(Math::random); //error
굳이 필요하다면, 아래와 같이 mapToInt()와 같은 메서드로 변환을 해야한다.(Stream, IntStream변환)
IntStream evenStream = Stream.iterate(0, n->n+2).mapToInt(Integer::valueOf);
Stream<Integer> stream = evenStream.boxed();