본문 바로가기
Book

[독서 기록] 모던 자바 인 액션 8장 컬렉션 API 개선

by Renechoi 2023. 1. 9.
List<String> friends = new ArrayList<>(); 
friends.add("R");
friends.add("O");

List<String> friends = Arrays.asList("R, "O");

하지만 문제는 불변 객체로 생성된다는 점이다. 

 

변경 시도시엔 UnsupportedOperationException 발생 ! 

 

- 276p 

 

List 인터페이스를 조금 더 살펴보면 List.of의 다양한 오버로드 버전이 있다는 사실을 알 수 있다. 

static <E> list<E> of(E e1, E e2, E e3, E e4)
static <E> list<E> of(E e1, E e2, E e3, E e4 E e5)

 

왜 다음처럼 다중 요소를 받을 수 있도록 자바 API를 만들지 않은 것인지 궁금할 것이다. 

static <E> List<E> of(E... elements)

내부적으로 가변 인수 버전은 추가 배열을 할당해서 리스트로 감싼다. 따라서 배열을 할당하고 초기화하며 나중에ㅐ 가비지 컬렉션을 하는 비용을 지불해야 한다. 고정된 숫자의 요소를 API로 정의하므로 이런 비용을 제거할 수 있다. List.of로 열개 이상의 요소를 가진 리스트를 만들 수도 있지만 이 때는 가변 인수를 이용하는 메서드가 사용된다. 

- 278p 

 

 

두 가지 방법으로 맵을 초기화하기 

Map<String, Integer> ageOfFriends = Map.of("R", 30, "O", 25, "T", 26);
Map<String, Integer> ageOfFriends = Map.ofEntries(entry("R" , 30), entry("O", 25), entry("T", 26));

- 279p 

 

 

removeIf 를 사용하는 방법 

 

transactions.removeIf(transaction -> Character.isDigit(transaction.getReferenceCode().charAt(0)));

- 282p 

 

 

replaceAll을 이용해 리스트의 각 요소를 새로운 요소로 바꾸기 

referenceCodes.replaceAll(code -> Character.toUpperCase(code.charAt(0)) + code.substring(10));

- 282p 

 

 

 

map의 forEach 메서드를 이용하기 

ageOfFriends.forEach((friend, age -> sout(friend, age));

 

 -283p

 

정렬 메서드 

Entry.comparingByValue

Entry.comparingByKey

 

 

favouriteMovies
	.entrySet()
    .stream()
    .sorted(Entry.comparingByKey()
    .forEachOrdered(System.out::println);

- 284p 

 

 

getOrDefault로 null 방지하기 

 

- computeIfAbsent : 제공된 키에 해당하는 값이 없으면, 키를 이용해 새 값을 계산하고 맵에 추가한다

- computeIfPresent : 제공된 키가 존재하면 새 값을 계산하고 맵에 추가한다.

- compute : 제공된 키로 새 값을 계산하고 맵에 저장한다. 

 

 

Rachael에게 줄 영화 목록을 만든다고 가정해보자. 

 

String friend = "Rachael";
List<String> movies = friendsToMovies.get(friend);
if (movies == null) {
	movies = new ArrayList<>();
    friendsToMoves.put(friend, movies);
}

movies.add("Star wars"); 

sout(friendsToMovies);

computeIfAbsent는 키가 존재하지 않으면 값을 계산해 맵에 추가하고 키가 존재하면 기존 값을 반환한다. 

 

frendsToMovies.computeIfAbsent("Rachael", name -> new ArrayList<>()).add("Star wars");

- 285 ~ 286p 

 

 

remove

replaceAll

putAll 

 

중복된 값이 없을 때는 putAll을 사용하면 된다. 

 

Map<String, String> family = Map.ofEntries(entry("Teo", "Star Wars"), entry("Cristina", "James Bond"));

Map<String, String> friends = Map.ofEntries(entry("Raphael", "Star Wars"));

Map<String, String> everyone = new HashMap<>(family);
eveyone.putAll(firends);

System.out.println(everyone);

 

하지만 중복키가 있다면 merge를 이용해야 한다. 

 

 

Map<String, String> family = Map.ofEntries(entry("Teo", "Star Wars"), entry("Cristina", "James Bond"));

Map<String, String> friends = Map.ofEntries(entry("Raphael", "Star Wars", "Cristina", "Matrix"));

Map<String, String> everyone = new HashMap<>(family);

friends.forEach((k,v) -> everyone.merge(k, v, (movie1, movie2) -> move1 + "&" movie2)); // 중복된 킥가 있으면 두 값을 연결한다.

=> return : Raphael=Star wars, Cristina=James Bond & Matrx, Teo = Star Wars

 

- 287 ~ 288p 

 

 

초기화 검사를 구현하기. 영화가 이미 존재하는지를 확인하고 몇 번 시청했는지 기록하기 

 

Map<String, Long> moviesToCount = new HashMap<>();

String movieName = "JamesBond";
long count = moivesToCount.get(movieName);

if (count == null) { 
	movieToCount.put(movieName, 1);
    } else {
    	moviesToCount.put(movieName, count + 1) 
    }
}

 

merge를 이용하기 

 

moviesToCount.merge(movieName, 1L, (key, count) -> count + 1L);

 

위 코드에서 merge의 두 번째 인수는 1L이다. 자바독에 따르면 이 인수는 "키와 연관된 기존 값에 합쳐질 널이 아닌 값 또는 값이 없거나 키에 널 값이 연관되어 있다면 이 값을 키와 연결"하는데 사용된다. 

 

=> 키의 반환값이 널이므로 처음에는 1

=> 그 다음부터는 값이 1로 초기화 되어 있으므로 +1 적용 

 

- 288 ~ 289p 

 

 

최신 버전의 ConcurrentHashMap 

- forEach

- reduce

- search

 

 

반응형