리스트와 집합에서 요소를 삭제하거나 바꾸는 관용 패턴을 적용시켜보자.
8.1 컬렉션 팩토리
자바에서 배열을 만들 때는
List<String> array = new ArrayList<>();
array.add("1");
array.add("2");
이런 식으로 많이 사용할 것이다.
여기서 코드를 더 줄이기 위해 Arrays.asList()로 고정 크기 리스트를 만들어보자.
List<String> array
= Arrays.asList("1", "2");
고정크기라서 새 요소를 추가하거나 요소를 삭제를 할 수는 없다.
만약 여기서 리스트의 요소를 갱신하거나 삭제를 한다면 UnsupportedOperationException이 발생한다.
UnsupportedOperationException 예외 발생
내부적으로 고정된 크기로 배열을 만들었을 때 이와 같은 일이 발생한다.
집합도 마찬가지로 Arrays.asSet()을 이용하면 같은 오류가 발생할 수 있다. 이 두 방법은 매끄럽지 못하고 불필요한 객체 할당을 필요로 한다.
8.1.1 리스트 만들기
자바 9에서 지원해주는 List.of , Set.of 팩토리 메서드를 이용해서 리스트를 만들 경우 고정된 크기여서 수정하려고 하면 오류가 발생한다.
맵 팩토리는 키와 값이 둘 다 들어 있어야 하는 컬렉션으로 단순 리스트나 집합보다는 복잡하다.
8.2 리스트와 집합 처리
자바 8부터는 List, Set 인터페이스에 removeIf. replaceAll. sort를 추가하여 리스트를 바꿀 수 있는 기능들을 추가하였다.
- removeIf: 프레디케이트(조건)를 만족하는 요소를 제거한다. List나 Set을 구현하거나 그 구현을 상속받은 모든 클래스에 이용할 수 있다.
- replaceAll: 리스트에서 이용할 수 있는 기능으로 요소를 바꿀 수 있다.
- sort: List 인터페이스에서 제공하는 기능으로 리스트를 정렬한다.
8.3 맵 처리
맵에서 키와 값을 반복하면서 요소를 만드는 작업은 귀찮을 것이다. 그런 불편함을 덜어주고자 forEach 메서드를 지원하여 코드를 간결하게 만들어준다.
ageFriends.forEach((friend, age) -> System.out.println(friend + "is" + age + "years old"));
8.3.1 정렬 메서드
두 개의 새로운 유틸리티를 이용하여 맵의 항목을 값 또는 키를 기준으로 정렬할 수 있다.
- Entry.comparingByValue
- Entry.comparingByKey
favorites
.entrySet()
.stream()
.sorted(Entry.comparingByKey) //key값으로 정렬시킬 수 있다.
.forEachOrdered(System.out.println);
이렇게 정렬 요청을 할 때 키가 맵에 존재하지 않을 때는 getOrdefault 메서드를 이용하여 처리할 수 있다.
8.3.2 getOrdefault 메서드
키가 존재하지 않으면 널이 반환되어 NPE 오류가 발생한다. 이 문제는 기본값을 반환하는 방식으로 해결할 수 있다.
getOrDefault 메서드는 첫 번째 인수로 키를, 두 번째 인수로 기본값을 받으며 맵에 키가 존재하지 않으면 두 번째 인수로 받은 기본값을 반환한다.
8.3.3 패턴 정리
맵에 키가 존재하는지 여부에 따라 동작을 실행하고 결과를 저장해야 하는 상황이 필요할 때
- computeIfAbsent: 제공된 키에 해당하는 값이 없으면, 키를 이용해 새 값을 계산하고 맵에 추가한다.
- computeIfPresent: 제공된 키가 존재하면 새 값을 계산하고 맵에 추가한다.
- compute: 제공된 키로 새 값을 계산하고 맵에 저장한다.
if (movie == null) {
movie = new ArrayList<>();
toMovie.put(friend, movie);
}
movie.add("123");
기존에 하던 null체크 방식을 computeIfAbsent를 이용하여 코드를 줄일 수 있다.
toMovie.computeIfAbsent("111", name -> new ArrayList<>())
.add("123");
computeIfAbsent 메서드는 현재 키와 관련된 값이 맵에 존재하며 널이 아닐 때만 새 값을 계산한다.
- 항목을 제거하고 싶다면 remove(key, value)를 이용하여 삭제할 수 있다.
- 항목을 교체하고 싶다면 replaceAll, replace를 이용하여 교체할 수 있다.
8.4 개선된 ConcurrentHashMap
ConcurrentHashMap 클래스는 내부 자료구조의 특정 부분을 잠글 수 있고, 갱신을 허용한다. 기존 버전보다 읽기 쓰기 연산 성능이 월등하다.
8.4.1 리듀스와 검색
ConcurrentHashMap은 세 가지 새로운 연산을 제공해준다.
- forEach: 각 (키, 값) 쌍에 주어진 액션을 실행
- reduce: 모든 (키, 값) 쌍을 제공된 리듀스 함수를 이용해 결과로 합침
- search: null이 아닌 값을 반환할 때까지 각 (키, 값) 쌍에 함수를 적용
- 키로 연산 하고 싶다면 뒤에 Key, 값으로 연산하고 싶다면 뒤에 Value, 객체로 연산하고 싶다면 뒤에 Entry를 붙여주면 된다.
'자바 > 모던 자바 인 액션' 카테고리의 다른 글
[모던 자바 인 액션] 10장. 람다를 이용한 도메인 전용 언어 (0) | 2022.12.27 |
---|---|
[모던 자바 인 액션] 9장. 리팩터링, 테스팅, 디버깅 (0) | 2022.11.18 |
[모던 자바 인 액션] 7장. 병렬 데이터 처리와 성능 (0) | 2022.10.24 |
[모던 자바 인 액션] 6장. 스트림으로 데이터 수집(2) (0) | 2022.10.24 |
[모던 자바 인 액션] 6장. 스트림으로 데이터 수집 (1) (0) | 2022.10.07 |