03.스트림과 람다를 이용한 효과적 프로그래밍
컬렉션 API 개선
컬렉션 팩토리
반환 객체는 불변
List Factory
//Before
List<String> friends = Arrays.asList("Park", "Kim", "Jeong");
//After
List<String> friends = List.of("Park", "Kim", "Jeong")Set Factory
Set<String> friends = Set.of("Park", "Kim", "Jeong");Map Factory
// 열 개 이하의 키/값 쌍을 가진 작은 맵을 만들 경우
Map<String, Integer> ageOfFriends = Map.of("Park", 20, "Kim", 21, "Jeong", 25);
// 그 이상의 맵 생성 (Map.enty: Map.Entry 객체를 만드는 새로운 팩토리 메서드)
import static java.util.Map.entry;
Map<String, String> ageOfFriends = Map.ofEntries(
entry("Park", 20),
entry("Kim", 21),
entry("Jeong", 25));리스트와 집합 처리
기존 컬렉션 객체와 Iterator 객체를 혼용한 삭제, 수정은 쉽게 문제를 일으켰었다.
그래서, java8에서는 List, Set Interface 에 새로운 메서드가 추가되었다. (기존 컬렉션 자체를 수정)
removeIf
Predicate를 만족하는 요소 제거 (List, Set)
replaceAll
UnaryOperator 함수를 이용하여 요소 교체 (List)
sort
리스트 정렬 (List)
맵 처리
1. forEach
기존 맵에서 키/값을 반복하며 확인했다면, forEach 메서드를 사용해보자.
2. Order
Entry.comparingByValue / Entry.comparingByKey
3. getOrDefault
찾으려는 키가 존재하지 않으면 기본값을 반환 getOrDefault(key, default)
4. Compute Pattern
computeIfAbsent
제공된 키에 해당하는 값이 없으면(값이 없거나 널), 키를 이용해 새 값을 계산하고 맵에 추가
현재 키와 관련된 값이 맵에 존재하며 널이 아닐 때만 새 값을 계산
computeIfPresent
제공된 키가 존재하면 새 값을 계산하고 맵에 추가
compute
제공된 키로 새 값을 계산하고 맵에 저장
5. Remove Pattern
6. Replace Pattern
replaceAll
BiFunction 을 적용한 결과로 각 항목의 값을 교체
Replace
키가 존재하면 맵의 값 교체
7. Merge
두 개의 맵에서 값을 합치거나 교체할 경우 사용
중복된 키가 없을 경우
중복된 키가 있을 경우
초기 검사 구현이 필요할 경우
지정된 키와 연관된 값이 없거나 널이면, 두 번째 인수를 키와 연결
그렇지 않을 경우, 세 번째 인자의 BiFunction을 적용
ConcurrentHashMap
ConcurrentHashMap 클래스는 동시 친화적이며 내부 자료구조의 특정 부분만 잠궈 동시 추가, 갱신 작업을 허용
forEach
각 (키/값) 쌍에 주어진 액션 실행
reduce
모든 (키/값) 쌍을 제공된 리듀스 함수를 이용해 결과로 합침
search
널이 아닌 값을 반환할 떄까지 각 (키/값) 쌍에 함수 적용
(키/값) 인수를 이용한 네 가지 연산 형태 지원
ConcurrentHashMap 상태를 잠그지 않고 연산을 수행하므로, 연산에 제공한 함수는 계산이 진행되는 동안 바뀔 수 있는 객체, 값, 순서 등에 의존하면 안 됨!
키/값으로 연산 :
forEach,reduce,search키로 연산 :
forEachKey,reduceKeys,searchKeys값으로 연산 :
forEachValue,reduceValues,searchValuesMap.entry 객체로 연산 :
forEachEntry,reduceEntries,searchEntriess
Count
mappingCount
맵의 매핑 개수를 반환 (매핑 개수가 int의 범위를 넘어서는 이후의 상황 대처)
리팩터링, 테스팅, 디버깅
리팩터링
코드 가독성 개선
코드 가독성이 좋다?란 '어떤 코드를 다른 사람도 쉽게 이해할 수 있음'을 의미한다.
1. 익명 클래스를 람다 표현식으로 리팩터링하기
익명 클래스에서 사용한 this와 super는 람다 표현식에서 다른 의미를 갖는다.
익명 클래스에서 this는 익명 클래스 자신
람다에서 this는 람다를 감싸는 클래스
익명 클래스는 감싸고 있는 클래스의 변수를 가릴 수 있다.
람다 표현식으로는 불가
2. 람다 표현식을 메서드 참조로 리팩터링하기
3. 명령형 데이터 처리를 스트림으로 리팩터링하기
스트림 API로 데이터 처리 파이프라인의 의도를 더 명확하게 보여주자.
4. 코드 유연성 개선
람다 표현식을 이용해서 동작 파라미터화를 쉽게 구현해보자.
함수형 인터페이스 적용
람다 표현식을 이용하기 위해 함수형 인터페이스를 추가
조건부 연기 실행
실행 어라운드
준비, 종료 과정을 처리하는 로직을 재사용하여 코드 중복 줄이기
람다 테스팅
Example
1. 보이는 람다 표현식의 동작 테스팅
람다는 익명이므로 테스트 코드 이름을 호출할 수 없다.
필요 시
람다를 필드에 저장해서 재사용하거나, 람다 로직 테스트가 가능하다.람다 표현식은 함수형 인터페이스의 인스턴스를 생성하므로
인스턴스의 동작으로 람다 표현식을 테스트할 수 있다
2. 람다를 사용하는 메서드의 동작에 집중하자
람다의 목표는 정해진 동작을 다른 메서드에서 사용할 수 있도록,
하나의 조각으로 캡슐화하는 것세부 구현을 포함하는 람다 표현식을 공개하지 말아야 한다.
3. 복잡한 람다를 개별 메서드로 분할하기
많은 로직을 포함하는 복잡한 람다 표현식의 경우, 람다 표현식을 메서드 참조로 바꾸자.
4. 고차원 함수 테스팅
메서드가 함수를 인수로 받을 경우, 다른 람다로 메서드 동작을 테스트
다른 함수를 반환하는 메서드의 경우, 함수형 인터페이스의 인스턴스로 간주하고 함수의 동작을 테스트
디버깅
1. 스택 트레이스 확인하기
스택 트레이스를 통해 프로그램이 어디서, 어떻게 멈추게 되었는지 살펴보자.
다만, 람다 표현식은 이름이 없어서 복잡한 스택 트레이스가 생성된다.
메서드 참조를 사용해도 스택 트레이스에서는 메서드명이 나타나지 않는다.
따라서, 람다 표현식과 관련한 스택 트레이스는 이해하기 어려울 수 있다.
2. 정보 로깅
peek스트림 연산을 활용하여 스트림 파이프라인에 적용된 각 연산이 어떤 결과를 도출하는지 확인할 수 있다.
Last updated