728x90
반응형
1. 람다식 (Lambda)
- 메서드를 하나의 식으로 표현한 것
- 람다식은 함수를 간략함녀서도 명확한 식으로 표현할 수 있게 해줌
- 메서드의 이름과 반환값이 없어지므로, 람다식을 익형 함수라고도 함
int[] arr = new int[5];
Arrays.setAll(arr, (i) -> (int)(Math.random() * 5) + 1); // 람다식 사용
// 위와 동일한 기능을 하는 함수
int method() {
return (int)(Math.random() * 5) + 1;
}
- 람다식은 메서드의 매개변수로 전달되는 것이 가능하고, 메서드의 결과로 반환될 수 있음
- 람다식으로 인해 메서드를 변수처럼 다룰 수 있음
장점
- 함수의 이름을 정의하지 않아도 사용 가능
- 문법이 간결해서 용이함
단점
- 람다식을 활용해 만든 익명 함수는 재사용이 불가능
- 람다만을 사용하면 비슷한 메서드가 중복되어 생성될 수 있어 오히려 코드의 가독성을 저해할 수 있음
예시
ArrayList<String> strList = new ArrayList<>(Arrays.asList("korea", "japan", "china", "france", "england"));
// 람다식을 활용한 맵핑 수행 (소문자 String 데이터들을 대문자화시키는 작업 수행)
Stream<String> stream = strList.stream();
stream.map(str -> str.toUpperCase()).forEach(System.out::println);
2. 스트림 (Stream)
- 쉽게 말해 람다를 잘 활용할 수 있는 기법 중 하나
- 스트림을 이용하면 선언형으로 컬렉션 데이터를 처리할 수 있음
- 멀티스레드 코드를 구현하지 않아도 데이터를 투명하게 병렬로 처리할 수 있음
장점
- 필터링, 데이터 변경 및 타입 변환, 자료구조 변환을 가능하게 함
- 스트림은 데이터를 변경하지 않음
- 내부적으로 작업을 반복 처리하므로, 필요에 따라 유용한 기법이 될 수 있음
단점
- 데이터의 변경이 필요할 때는 유용하지 않음
- 작업의 반복처리가 필요하지 않을 때는 유용하지 않음
- 컬렉션의 구성요소를 모두 읽은 후에는 재사용이 불가능함 (재활용성이 떨어짐)
스트림 활용 과정
1. 스트림 생성
- Collection.stream()을 이용해 원하는 타입의 컬렉션을 기반으로 스트림을 생성함
2. 중간 연산
중간 연산 설명
Steam<T> distinct() | 중복 제거 |
Stream<T> filter() | 조건 안 맞는 요소 제외 |
Stream<T> limit() | 스트림 일부 잘라냄 |
Stream<T> skip() | 스트림 일부 건너뜀 |
Stream<T> peek() | 스트림 요소에 작업 수행 |
Stream<T> sorted() | 스트림 요소 정렬 |
Stream<T> map() | 원하는 요소만 뽑거나 특정 형태로 변환 |
3. 최종 연산
- 스트림이 한 번 결과를 반환하면, 이후에는 재사용이 불가능함
- 최종 연산 결과 값은 하나의 값이거나, 배열 혹은 다른 유형의 컬렉션 데이터일 수 있음
- 예를 들어 collect() 메서드를 활용해 다른 컬렉션 유형으로 반환할 수 있음
예시
public class Main {
public static void main(String[] args) {
// List 컬렉션 및 제너릭스를 활용하여 배열 생성
List<String> list = new ArrayList<>();
// 배열에 데이터 추가
list.add("서울");
list.add("부산");
list.add("속초");
list.add("서울");
// 스트림 활용 과정 예제 (스트림을 활용해 list에서 set으로 데이터를 변환)
List<String> result = list.stream() // 1. List 스트림 생성
.limit(2) // 2. 중간 연산 (데이터 사이즈를 축소 및 나머지 데이터 삭제)
.collect(Collectors.toList()); // 3. 최종 연산 (결과값을 리스트화)
Set<String> set = list.stream() // 1. Set 스트림 생성 (새로운 List 스트림 생성)
.filter("서울"::equals) // 2. 중간 연산 ("서울" 데이터만 필터링)
.collect(Collectors.toSet()); // 3. 최종 연산 (필터링한 데이터를 셋으로 변환)
set.forEach(System.out::println); // 람다식을 활용하여 결과출력 (셋의 데이터를 출력)
}
}
이펙티브 자바 - 람다와 스트림
익명 클래스보다는 람다를 사용하라
- 익명 클래스의 인스턴스를 함수 객체로 사용하는 방법은 낡은 기법이다!
Collections.sort(words, new Comparator<String>() {
public int compare (String s1, String s2) {
return Integer.compare(s1.length(), s2.length();
}
});
- 지금은 함수형 인터페이스라 부르는 이 인터페이스들의 인스턴스를 람다식을 사용해 만들 수 있게 됨
- 람다식을 함수 객체로 사용 - 익명 클래스 대체!
Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
- 타입을 명시해야 코드가 더 명활할 때만 제외하고는, 람다의 모든 매개변수 타입은 생략하자!
- 추가적으로 아래와 같이 비교자 생성 메서드를 사용하면 더 간결하게 만들 수 있음
Collections.sort(words, comparingInt(String::length));
- 더 나아가서 자바 8에서 List 인터페이스에 추가된 sort 메서드를 이용하면 더욱 간결해짐
words.sort(comparingInt(String::length));
- 람다는 이름이 없고 문서화도 못 함
- 따라서 코드 자체로 동작이 명확히 설명되지 않거나 코드 라인 수가 많아지면 람다를 쓰지 말아야 함!
- 한 줄일 때 베스트, 세 줄 안에 끝내는 게 좋음
- 람다는 함수형 인터페이스에서만 사용되기 때문에 익명 클래스를 완전히 대체할 수는 없음
- 익명 클래스는 (함수형 인터페이스가 아닌) 타입의 인스턴스를 만들 때만 사용하라!
참고) 함수형 인터페이스란?
추상 메서드가 1개만 정의된 인터페이스를 통칭
728x90
반응형
댓글