모던 자바 인 액션 8장에서는 개선된 컬렉션에 대해 소개한다.
개선된 사항, 컬렉션 팩토리, 새로운 기능들에 대해 자세히 살펴본다.
8.1 컬렉션 팩토리
자바 9에서는 작은 컬렉션 객체를 쉽게 만들 수 있는 방법들을 제공한다.
이와 같은 기능은 다음과 같이 코드를 간단하게 줄일 수 도 있고,
고정 크기의 리스트를 만들기 때문에 의도치 않게 컬렉션이 변화되지 않도록 막을 수 있다. (UnsupportedOperationException 발생)
List<String> friends = new ArraysList<>();
friends.add("Raphael");
friends.add("Olivia");
friends.add("Thibaut");
List<String> friends = Arrays.asList("Raphael", "Olivia", "Thibaut");
- 리스트 팩토리 (
List.of)- 변경할 수 없는 리스트 (추가하려고 하면
UnsupportedOperationException발생) null요소 금지List<String> friends = List.of("Raphael", "Olivia", "Thibaut");
- 변경할 수 없는 리스트 (추가하려고 하면
- 집합 팩토리 (
Set.of)- 변경할 수 없는 집합
- 중복된 요소를 제공 불가 (
IllegalArgumentException발생)Set<String> friends = Set.of("Raphael", "Olivia", "Thibaut");
- 맵 팩토리 (
Map.of,Map.ofEntries)- 변경할 수 없는 맵
- 키와 값이 열개 이하인 경우
Map.of, 이상인 경우Map.ofEntries사용Map<String, Integer> ageOfFriends = Map.of("Raphael", 30, "Olivia", 25, "Thibaut", 26); Map<String, Integer> ageOfFriends = Map.ofEntries(entry("Raphael", 30), entry("Olivia", 25), entry("Thibaut", 26));
8.2 리스트와 집합 처리
removeIf- 프레디케이트를 만족하는 요소를 제거
- 기존 컬렉션을 변경
- 코드가 단순해지고
ConcurrentMOdificationException버그 방지
replaceAllList에서 이용할 수 있으며UnaryOperator함수로 각 요소를 변경- 새로운 컬렉션을 생성하지 않고 간단하게 변경 가능
8.3 맵 처리
forEach메서드BiConsumer를 인수로 받는 키와 값을 반복하면서 확인하는 작업- 기존에는
Map.Entry<K, V>을 이용함ageOfFriends.forEach((friend, age) -> System.out.println(friend + " is " + age + " yeas old"));
- 정렬 메서드 (
Entry.comparingByValue,Entry.comparingByKey)- 맵의 항목을 값 또는 키를 기준으로 정렬
favoriteMovies.entrySet() .stream() .sorted(Entry.comparingByKey()) .forEachOrdered(System.out::println); // Cristina = Matrix // Olivia = James Bond // Raphael = Star Wars
- 맵의 항목을 값 또는 키를 기준으로 정렬
getOrDefault메서드- 첫 번째 인수는 키, 두 번째 인수는 기본값을 받고 키가 존재하지 않으면 기본값 반환
- 기존에는
null이 반환되어NullPointerException발생될 여지가 있었음// matrix 출력 System.out.println(favoriteMovies.getOrDefault("not exists key", "matrix"))
- 계산 패턴(
computeIfAbsent,computeIfPresent,comput)computeIfAbsent: 제공된 키에 해당하는 값이 없으면 키를 이용해 새 값을 계산하고 추가computeIfPresent: 제공된 키가 존재하면 새 값을 계산하고 맵에 추가comput: 제공된 키로 새값을 계산하고 맵에 저장
- 삭제 패턴 (
remove)- 기존에는
remove는 삭제만 되었지만 자바 8에서는 특정한 키 값과 연관된 경우에만 제거하는 메서드가 제공된다.// 키와 값이 동일한 경우에만 삭제 favouriteMovies.remove(key, value);
- 기존에는
- 교체 패턴 (
replaceAll,Replace)replaceAll: 모든 항목의 값을BiFunction을 적용한 결과들로 교체Replace: 키가 존재하면 맵의 값을 변경 (키가 특정 값으로 매핑된 경우에도 교체 가능)
- 합침
merge를 이용하면 값을 유연하게 합칠 수 있음putAll을 이용하면 합쳐지긴 하지만 중복된 키에 대한 처리 불가능// 두번째 인수는 키가 없거나 반환 값이 null 인 경우 기존 값으로 사용 // 세번째 인수는 중복된 키가 어떻게 합쳐질 지 결정하는 BIFunction moviesToCount.merge(movieName, 1L, (key, count) -> count + 1L)
8.4 개선된 ConcurrentHashMap
ConcurrentHashMap 은 자료구조의 특정 부분만 잠궈서 동시 추가, 갱신 작업을 허용하여,
Hashtable 버전에 비해 읽기 쓰기 연산 성능이 뛰어나다.
- 리듀스와 검색
- 키와 값으로 연산
forEach: 각 키와 값에 주어진 액션을 실행reduce: 모든 키와 값을 제공된 리듀스 함수를 이용해 합침search: 널이 아닌 값을 반환할 때까지 각 키와 값에 함수 적용
- 키로 연산(
forEachKey,reduceKey,searchKey) - 값으로 연산(
forEachValue,reduceValues,searchValues) - Map.Entry 객체로 연산(
forEachEntry,reduceEntries,searchEntries) - 함수를 실행할 때는 병렬성 기준값(threshold) 지정 필요
- 맵의 크기가 기준 값보다 작으면 순차적으로 연산 실행
- 기준값을 1로 지정하면 공통 스레드 풀을 이용해 병렬성 극대화
- 키와 값으로 연산
- 계수(
mappingCount)int범위를 넘어설 수 있어서size메서드 대신mappingCount메서드 사용
- 집합뷰(
newkeySet)keySet으로 생성한 집합은 맵과 서로 변경에 대해 영향을 받음- 서로 영향 없이 유지할 수 있도록
newKeySet메서드 사용