티스토리 뷰

728x90

컬렉션 팩토리

작은 컬렉션 객체를 쉽게 만들 수 있는 방법

List<String> friends = Arrays.asList("Raphael", "Olivia", "Thibaut");

요소를 갱신할 순 있지만 추가하거나 삭제하려면 UnsupportedOperationException이 발생한다.

내부적으로 고정된 크기의 변환할 수 있는 배열로 구현되었기 때문에 이와 같은 일이 일어난다.

리스트 팩토리

List.of 팩토리 메소드를 이용해서 간단하게 리스트를 만들 수 있다.

List<String> friends = List.of("Raphael", "Olivia", "Thibaut");

List.of 으로 만든 리스트에 수정 또는 삭제를 할 때 UnsupportedOperationException이 발생한다.

컬렉션 팩토리 메서드 대신 스트림 API를 사용해 리스트를 만드는것이 좋을까? 답은 데이터 처리 형식을 설정하거나 데이터를 변환할 필요가 없다면 사용하기 간편한 팩토리 메서드를 이용할 것을 권장한다. 팩토리 메서드 구현이 더 단순하고 목적을 달성하는데 충분하기 때문이다.

집합 팩토리

집합은 오직 고유의 요소만 포함할 수 있다. 같은 요소를 포함해 집합을 만드려고하면 IllegalArgumentException이 발생한다.

Set<String> friends = Set.of("Raphael", "Olivia", "Thibaut");

맵 팩토리

Map.of 팩토리 메서드에 키와 값을 번갈아 제공하는 방법으로 맵을 만들 수 있다.

Map<String, Integer> ageOfFriends = Map.of("Raphael", 30, "Olivia", 25, "Thibaut", 26);
//{Raphael=30, Thibaut=26, Olivia=25}

열 개 이하의 키와 값 쌍을 가진 작은 맵을 만들때는 Map.of 가 유용하지만 그 이상의 맵은 Map.ofEntries 팩토리 메서드를 이용하는 것이 좋다.

import static java.util.Map.entry;

Map<String, Integer> ageOfFriends2 = Map.ofEntries(
    entry("Raphael", 30),
    entry("Olivia", 25),
    entry("Thibaut", 26));

Map.entry는 Map.Entry 객체를 만드는 새로운 팩토리 메서드다.

리스트와 집합 처리

  • removeIf
    • 프레디케이트를 만족하는 요소를 제거한다.
  • replaceAll
    • 리스트에서 이용할 수 있는 기능으로 UnaryOperator 함수를 이용해 요소를 바꾼다.
  • sort
    • List 인터페이스에서 제공하는 기능으로 리스트를 정렬한다.

새로운 결과를 만드는 스트림 동작과 달리 이들 메서드는 기존 컬렉션을 바꾼다. 기존 컬렉션을 바꾸는 동작은 에러를 유발하며 복잡함을 더하기 때문에 자바 8에서 removeIf와 replaceAll가 추가되었다.

맵 처리

자바 8에서는 Map 인터페이스에 몇 가지 디폴트 메서드를 추가 했다. 자주 사용되는 패턴을 개발자가 직접 구현할 필요가 없도록 이들 메서드를 추가한 것이다.

forEach 메서드

Map<String, Integer> ageOfFriends = Map.of("Raphael", 30, "Olivia", 25, "Thibaut", 26);
//기존 자바
for (Map.Entry<String, Integer> entry: ageOfFriends.entrySet()) {
  String friend = entry.getKey();
  Integer age = entry.getValue();
  System.out.println(friend + " is " + age + " years old");
}
System.out.println("--> Iterating a map with forEach()");

//자바8
ageOfFriends.forEach((friend, age) -> System.out.println(friend + " is " + age + " years old"));

정렬 메서드

다음 두 개의 새로운 유틸리티를 이용하면 맵의 항목을 값 또는 키를 기준으로 정렬할 수 있다.

  • Entry.comparingByValue
  • Entry.comparingByKey
Map<String, String> favouriteMovies = Map.ofEntries(
    entry("Raphael", "Star Wars"),
    entry("Cristina", "Matrix"),
    entry("Olivia", "James Bond"));

favouriteMovies.entrySet().stream()
    .sorted(Entry.comparingByKey())
    .forEachOrdered(System.out::println);  //사람의 이름을 알파벳 순으로 스트림 요소를 처리한다.

//Cristina=Matrix
//Olivia=James Bond
//Raphael=Star Wars

getOrDefault 메서드

찾으려는 키가 존재하지 않으면 널이 반환되므로 결과가 널인지 확인해야 했지만 getOrDefault 방식으로 이 문제를 해결할 수 있다.

이 메서드는 첫 번째 인수로 키를, 두 번째 인수로 기본값을 받으며 맵에 키가 존재하지 않으면 두 번째 인수로 받은 기본값을 반환한다.

Map<String, String> favouriteMovies = Map.ofEntries(
    entry("Raphael", "Star Wars"),
    entry("Cristina", "Matrix"),
    entry("Olivia", "James Bond"));
favouriteMovies.entrySet().stream()
    .sorted(Entry.comparingByKey())
    .forEachOrdered(System.out::println);

System.out.println(favouriteMovies.getOrDefault("Olivia", "Matrix")); //James Bond
System.out.println(favouriteMovies.getOrDefault("Thibaut", "Matrix")); //Null이므로 Matrix

키가 존재하더라도 값이 널인 상황에서는 Null을 반환한다. 즉 키가 존재하느냐의 여부에 따라서 두 번째 인수가 반환될지 결정된다.

계산 패턴

맵에 키가 존재하는지 여부에 따라 어떤 동작을 실행하고 결과를 저장해야 할 때 다음의 세 가지 연산이 이런 상황에 도움을 준다.

  • computeIfAbsent
    • 제공된 키에 해당하는 값이 없으면(값이 없거나 널), 키를 이용해 새 값을 계산하고 맵에 추가한다.
  • computeIfPresent
    • 제공된 키가 존재하면 새 값을 계산하고 맵에 추가한다.
  • compute
    • 제공된 키로 새 값을 계산하고 맵에 저장한다.

삭제 패턴

제공된 키에 해당하는 맵 항목을 제거하는 remove 메서드와 더불어, 키가 특정한 값에 연관되어 있을 때만 항목을 제거하면 오버로드 버전 메서드를 제공한다.

//java.util.Map
default boolean remove(Object key, Object value) {
    Object curValue = get(key);
    if (!Objects.equals(curValue, value) ||
        (curValue == null && !containsKey(key))) {
        return false;
    }
    remove(key);
    return true;
}

교체 패턴

맵의 항목을 바꾸는 데 사용할 수 있는 두 개의 메서드가 맵에 추가

  • replaceAll
    • BiFunction을 적용한 결과로 각 항목의 값을 교체한다.
  • replace
    • 키가 존재하면 맵의 값을 바꾼다. 키가 특정 값으로 매핑되었을 때만 값을 교체하는 오버로드 버전도 있다.
Map<String, String> favouriteMovies = new HashMap<>();
favouriteMovies.put("Raphael", "Star Wars");
favouriteMovies.put("Olivia", "james bond");

favouriteMovies.replaceAll((friend, movie) -> movie.toUpperCase());
System.out.println(favouriteMovies);
//{Olivia=JAMES BOND, Raphael=STAR WARS}

합침 merge

두 개의 맵을 합칠 때 값을 좀 더 유연하게 합쳐야 한다면 새로운 merge 메서드를 이용할 수 있다.

이 메서드는 중복된 키를 어떻게 합칠지 결정하는 BiFunction을 인수로 받는다.

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);
friends2.forEach((k, v) -> everyone.merge(k, v, (movie1, movie2) -> movie1 + " & " + movie2)); //중복된 키가 있으면 두 값을 연결

System.out.println(everyone);
//{Raphael=Star Wars, Cristina=James Bond & Matrix, Teo=Star Wars}

자바 독의 merge 메서드 설명

“지정된 키와 연관된 값이 없거나 값이 널이면 merge는 키를 널이 아닌 값과 연결한다. 아니면 merge는 연결된 값을 주어진 매핑 함수의 결과 값으로 대치하거나 결과가 널이면 항목을 제거한다.”

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함