컬렉션 팩토리 및 리스트와 집합, 맵의 새로운 관용패턴이 어떤게 있는지 정보를 제공해주는 챕터이다.
// 우리는 기존에 리스트를 이런 형태로 주로 가져왔다. List<String> friends = new ArrayList<>(); friends.add("Raphael"); friends.add("Olivia"); friends.add("Thibaut"); // 이상태로 실행하면 오류가 발생한다. List<String> friends = Arrays.asList("Raphael", "Olivia"); friends.set(0, "Rechard"); friends.add("Thibaut"); Set<String> friends = Stream.of("Raphael", "Olivia","Thibaut") .collect(Collector.toSet());
하지만 이렇게 하면, 새로운 요소를 추가할 때 UnsupportedOperationException이 발생한다.
이유는 고정된 크기의 변환할 수 있는 배열로 내부가 구성되어있기 때문이다.
팩토리 메서드 역시 마찬가지로 고정된 크기이고, 더군다나 데이터 변경이 안되 오히려 기능적으로는 안좋다는 단점이 있다.
하지만 그래도 쓰는 이유가 뭘까? 바로 불필요한 객체 할당을 줄여준다
// 리스트 팩토리 메서드 List<String> friends = List.of("Raphael", "Olivia","Thibaut"); // 집합 팩토리 메서드 Set<String> friends = Set.of("Raphael", "Olivia","Thibaut"); // 맵 팩토리 메서드 Map<String, Integer> ageOfFriends = Map.of("Raphael", 30, "Olivia", 25, "Thibaut", 26);
컬렉션의 변경과 추가생성은 안된다.
- remove if : 프레디케이트를 만족하는 요소를 제거하고 나머지 요소만 저장
- removeAll : 리스트에서 이용할 수 있는 기능으로 UnaryOperator 함수를 이용해 요소를 바꾼다.
- sort : List 인터페이스에서 제공하는 기능으로 리스트 정렬
// removeif transactions.removeIf(transaction -> Character.isDigit(transaction.getReferenceCode().charAt(0))); // removeAll referenceCodes.replaceAll(code -> Character.toUpperCase(code.charAt(0)) + code.subString(1));
- foreach 메서드 : 키와 value를 출력하기 위한 메서드
- 정렬 메서드 : value 또는 키를 기준으로 정렬
- getOrDefault 메서드 : 키가 존재하지 않을 때 기본값을 넣는 메서드
// foreach ageOfFrends.forEach((friends, age) -> System.out.println(friend + " is " + age + " years old")); // sort favouriteMovies.entrySet().stream() .sorted(Entry.ComparingByKey()) .forEachOrdered(System.out::println); // getOrDefault(여담 코테볼때 나도 자주쓰는 편이다.) System.out.println(favorieMovies.getOrDefault("Olivia", "Matrix"));
- computeIfAbsent : 제공된 키에 해당하는 값이 없으면(또는 Null) 키를 애용해 값을 계산 후 맵에 추가
- computeIfPresent : 제공된 키가 존재하면 새 값을 계산 후 맵에 추가
- compute : 제공된 키로 새 값 계산 후 맵에 저장
- 제공 된 키에 해당하는 맵 항목을 제거하는 메서드
favoriteMovies.remove(key, value);
- 맵의 항목을 바꾸는데 사용할 수 있는 두개의 메서드가 맵에 추가
1-1. replaceAll : 각 항목의 값을 교체, List의 replaceAll과 유사
1-2. Replace : 키가 존재하면 맵의 값을 바꿈favoriteMovies.replaceAll((friend, movie) -> movie.toUpperCase());
두 개의 맵을 합칠 때 사용
1. merge : 중복 된 키가 있으면 두 값을 연결friends.forEach((k, v) -> everyone.merge(k, v, (movie1, movie2) -> movie1 + " & " + movie2));
최신 기술을 반영한 HashMap
foreach : 각 키와 value 쌍에 주어진 액션 실행
reduce : 모든 key value 쌍에 제공된 리듀스 함수 이용해 결과를 합침
search 널이 아닌 값을 반환할 때까지 각 키와 value 쌍에 함수 적용
예시// 기준값을 1로 세팅 시 공통 스레드 풀을 이용해 병렬성 극대화 // Long.MAX_VALUE를 기준값으로 설정하면 한 개의 스레드 연산 실행 // 맵의 최대값을 찾는 예시코드 Optional<Integer> maxValue = Optional.ofNullable(map.reduceValues(1, Long::max))
ConcurrentHashMap 클래스는 mappingCount 메서드를 제공, int를 반환하는 mappingCount의 장점은 int 범위를 넘어간 이후 상황을 대처할 수 있다는 것이 장점
ConcurrentHashMap<String,Integer> hash = new ConcurrentHashMap<>(); hash.mappingCount();
ConcurrentHashMap 클래스는 집합뷰로 반환하는 keySet 제공
ConcurrentHashMap.KeySetView<String, Integer> keySet = hash.keySet();
자바9에서는 List.of,Map.of 등 컬렉션 팩토리를 쓰면 객체를 생성을 안해도 된다는 장점이 있다는 것을 처음 알았다. 가독성도 높은만큼 작은 데이터를 처리할 때는 가끔 쓰면 좋을 것 같다는 생각이 들었다. 이번 챕터는 새로운것을 깨닫는 느낌보단 이렇게도 쓸 수 있구나 하는 API문서를 한번 훑어본 기분이었다.