메서드를 ‘하나의 식’으로 표현한 것이다.
메서드의 이름과 반환값이 없어지므로 ‘익명함수’라고도 한다.
메서드의 이름과 반환 타입을 제거하고 매개변수 선언부와 몸통 { } 사이에 “->
”를 추가한다.
장점
함수형 인터페이스를 타입으로 사용해 람다식을 변수로서 받을 수 있다.
Runnable runnable = new Runnable() {
@Override
public void run() {
// ~
}
);
Runnable runnable = () -> { ... }; // Lambda
이때 함수형 인터페이스를 @FunctionalInterface
를 통해 직접 정의할 수도 있지만, 일반적으로 java.util.function 패키지에 정의된 함수형 인터페이스를 사용한다.
java.util.function 패키지의 기본적인 함수형 인터페이스
함수형 인터페이스 | 메서드 | 반환타입 | 함수 디스크립터 | 설명 |
---|---|---|---|---|
java.lang.Runnable | run() | void | () -> void | 매개변수x, 반환값x |
Supplier | get() | T | () -> T | 매개변수x, 반환값o |
Consumer | accept(T t) | void | T -> void | 매개변수o, 반환값x |
Function<T, R> | apply(T t) | R | T -> R | 매개변수 1개, 반환값o |
Predicate | test(T t) | boolean | T -> boolean | 매개변수 1개, 반환값o |
… | … | … | … | … |
forEach()
는 Consumer 타입의 함수형 인터페이스를 매개변수로 받는다.
참고 영상
https://www.youtube.com/watch?v=u5sLXaWo7tY
https://www.youtube.com/watch?v=cHIKmmWFwng
Java8 API에서 추가된 람다를 사용하는 기술 중 하나이다.
선언형으로 컬렉션 데이터를 처리할 수 있도록 도와준다.
다양한 데이터 소스를 표준화된 방법으로 다룰 수 있다.
Collection Framework를 통해 관리하는 데이터를 처리하기 위해 주로 사용한다.
List<String> list = Arrays.asList("Lee", "Kim", "Park");
// 기존
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// stream 활용
list.stream().forEach(name -> System.out.println(name));
Stream API의 최상위 인터페이스는 BaseStream 인터페이스지만 직접 사용하지는 않는다.
Stream 인터페이스는 BaseStream을 상속하는 인터페이스이다.
여러 메소드들을 정의하고 있으며 많은 메소드들의 파라미터에 람다와 메소드 참조가 필요하다.
메소드 | 기능 |
---|---|
long count() | 해당 스트림에 포함된 항목의 수를 반환한다. |
Stream concat(Stream, Stream) | 파라미터로 전달되는 두 개의 스트림을 하나의 스트림으로 반환한다. |
R collect(Collector) | 스트림의 항목들을 컬렉션 타입의 객체로 반환한다. |
Stream filter(Predicate) | 스트림의 항목들을 파라미터의 조건에 따라 필터링하고 결과 항목들을 스트림 형태로 반환한다. |
void forEach(Consumer) | 스트림 항목들을 순회한다. |
Optional reduce(BinaryOperator) | 람다 표현식을 기반으로 데이터를 소모하고 그 결과를 반환한다. |
Object toArray() | 스트림 항목들을 배열 객체로 전환한다. |
Stream sorted() | 스트림 항목들에 대해 정렬하고 이를 스트림으로 반환한다. |
… | … |
Collection 객체 사용
처리할 데이터가 이미 존재하고, 이를 처리할 때 일반적으로 사용하는 방식이다.
List 인터페이스는 Collection 인터페이스를 상속받고, Collection에는 Stream 객체를 반환하는 stream()
default 메소드가 있다.
한번 생성한 Stream은 사용 후 다시 사용할 수 없고, 데이터에 대한 처리가 완료되면 종료된다.
List<String> list = Arrays.asList("Lee", "Kim", "Park");
Stream<String> stream = list.stream();
stream.forEach(name -> System.out.println(name));
Stream.Builder를 사용
데이터가 존재하지 않고, Stream을 만들었을 때, 직접 데이터를 추가해서 사용하고자 할 때 사용한다.
메소드 | 기능 |
---|---|
void accept(T) | 스트림 빌터에 데이터를 추가한다. |
Stream.Builder add(T) | 스트림 빌더에 데이터를 추가하고, 스트림을 반환한다. |
Stream build() | 스트림 빌더에 데이터 추가를 종료하고, 스트림을 반환한다. |
Stream.Builder<String> builder = Stream.builder();
builder.accept("Kim");
builder.accept("Lee");
builder.accept("Park");
Stream<String> stream = builder.build();
stream.forEach(name -> System.out.println(name));
참고 영상
https://www.youtube.com/watch?v=mUJGw_b6DfI&t=5s
https://www.youtube.com/watch?v=ASQDml4NyKM
스트림의 연산은 각 연산의 연결을 통해 파이프라인을 구성할 수 있다.
연산 처리는 스트림 객체의 생성, 중간 연산, 최종 연산 단계로 구분할 수 있다.
Collection Data -> Filter -> Sort -> Map -> Collect
filter
, map
과 같은 중간 연산은 Stream을 반환하고, 연속해서 호출하는 Method Chaining으로 구현 가능하다.
최종 연산이 실행되어야 중간 연산이 처리되므로, 중간 연산들로만 구성된 메소드 체인은 실행되지 않는다.
연산 | 반환 형식 | 연산 인수 |
---|---|---|
filter | Stream | Predicate |
map | Stream | Function<T, R> |
limit | Stream | |
sorted | Stream | Comparator |
distinct | Stream | |
peek | Stream | Consumer |
skip | Stream |
필터링
전체 데이터에서 불필요한 데이터를 없에고, 원하는 데이터를 추출하기 위한 과정으로 중간 연산이다.
Stream API의 filter()
, distinct()
와 같은 메소드를 이용하여 데이터 추출이나 중복 데이터를 제거한다.
이때 중복 데이터를 제거하는 distinct()
는 병렬 스트림에서의 성능과, equals()
메소드의 필요성을 고려해야 한다.
정렬
특정 조건에 따라 데이터를 정렬하고, 이를 다시 Stream으로 반환한다.
sorted()를 이용한 정렬을 위해서는 객체들이 Comparable 인터페이스를 구현한 클래스여야 한다.
또한 Comparator 인터페이스를 통해 다른 방식의 정렬도 사용 가능하다.
List<T> list = new ArrayList<>();
// Comparable을 통한 정렬의 경우 인자를 전달받지 않는다
list.stream()
.sorted()
.forEach(System.out::println);
// Comparator를 사용한 정렬의 경우 인자를 전달받는다.
list.stream()
.sorted(Comparator.comparing(T::getThings))
.forEach(System.out::printlin);
맵핑
스트림이 관리하는 데이터를 다른 형태의 데이터로 반환해준다.
map()
, mapToInt()
, mapToDouble()
, mapToLong()
등이 있다.
Function을 파라미터로 받는다.
List<Curtomers> customers = new ArrayList<>();
customers.add(new Customer("Kim", 33));
customers.add(new Customer("Lee", 28));
customers.add(new Customer("Park", 31));
customers.add(new Customer("Yoon", 25));
List<String> names = customers.stream()
.map(Customer::getName)
.collect(Collectors.toList());
연산 | 반환 형식 |
---|---|
forEach | 스트림의 각 요소를 소비하며 람다식을 적용하고, void형을 반환한다. |
count | 스트림 요소의 수를 Long형으로 반환한다. |
collect | List, Map 형태의 컬렉션을 반환한다. |
sum | 스트림의 모든 요소에 대한 합을 반환한다. |
reduce | 스트림의 요소를 하나씩 줄여가며 연산을 수행하고 결과를 Optional로 반환한다. |
allMatch | 파라미터로 전달되는 람다식 기준으로 데이터가 모두 일치하는지 확인한다. |
findFirst | 스트림 데이터 중 첫번째 데이터를 반환한다. |
reduce | 스트림 데이터 중 임의의 데이터를 반환한다. |
… | … |
List<String> list = Arrays.asList("Kim", "Lee", "Park", "Yoon");
list.stream()
.filter(name -> name.length() > 3) // 중간 연산
.sorted()
.forEach(System.out::println); // 최종 연산
참고 영상
https://www.youtube.com/watch?v=v7n66yvM8bw&list=TLPQMDkwMTIwMjSH9w8ohdh0rQ&index=2
https://www.youtube.com/watch?v=7cVPlgyx3d8
https://www.youtube.com/watch?v=0ZJr0O1DN8g