1. 개요

이 빠른 자습서에서는 Collectors 클래스 toMap () 메서드에 대해 설명합니다 . 이를 사용하여 Stream을 Map 인스턴스 수집 합니다.

여기에서 다루는 모든 예제에 대해 책 목록을 시작점으로 사용하고이를 다른 Map 구현 으로 변환합니다 .

2. List를 Map으로 변경

ListMap 으로 변환하여 가장 간단한 경우부터 시작하겠습니다 .

Book 클래스를 정의하는 방법은 다음과 같습니다 .

class Book {
    private String name;
    private int releaseYear;
    private String isbn;
    
    // getters and setters
}

그리고 코드의 유효성을 검사하기 위해 책 목록을 만들 것입니다.

List<Book> bookList = new ArrayList<>();
bookList.add(new Book("The Fellowship of the Ring", 1954, "0395489318"));
bookList.add(new Book("The Two Towers", 1954, "0345339711"));
bookList.add(new Book("The Return of the King", 1955, "0618129111"));

이 시나리오에서는 toMap () 메서드 의 다음 오버로드를 사용합니다 .

Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
  Function<? super T, ? extends U> valueMapper)

함께 toMap , 우리는 맵의 키와 값을 얻는 방법에 대한 전략을 나타낼 수 있습니다 :

public Map<String, String> listToMap(List<Book> books) {
    return books.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName));
}

그리고 우리는 그것이 작동하는지 쉽게 확인할 수 있습니다.

@Test
public void whenConvertFromListToMap() {
    assertTrue(convertToMap.listToMap(bookList).size() == 3);
}

3. 주요 문제 해결

위의 예는 잘 작동했지만 중복 키는 어떻게됩니까?

의 출시 연도에 따라 지도키를 입력했다고 가정 해 보겠습니다 .

public Map<Integer, Book> listToMapWithDupKeyError(List<Book> books) {
    return books.stream().collect(
      Collectors.toMap(Book::getReleaseYear, Function.identity()));
}

이전 도서 목록이 주어지면 IllegalStateException이 표시됩니다 .

@Test(expected = IllegalStateException.class)
public void whenMapHasDuplicateKey_without_merge_function_then_runtime_exception() {
    convertToMap.listToMapWithDupKeyError(bookList);
}

이를 해결하려면 추가 매개 변수 인 mergeFunction 과 함께 다른 메소드를 사용해야합니다 .

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
  Function<? super T, ? extends U> valueMapper,
  BinaryOperator<U> mergeFunction)

문제의 경우 기존 항목을 유지함을 나타내는 병합 함수를 소개하겠습니다.

public Map<Integer, Book> listToMapWithDupKey(List<Book> books) {
    return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),
      (existing, replacement) -> existing));
}

즉, 우리는 첫 번째 승리 행동을 얻습니다.

@Test
public void whenMapHasDuplicateKeyThenMergeFunctionHandlesCollision() {
    Map<Integer, Book> booksByYear = convertToMap.listToMapWithDupKey(bookList);
    assertEquals(2, booksByYear.size());
    assertEquals("0395489318", booksByYear.get(1954).getIsbn());
}

4. Map 종류들

기본적으로 toMap () 메서드는 HashMap 을 반환합니다 .

하지만 다른 Map 구현을 반환 할 수 있습니다 .

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
  Function<? super T, ? extends U> valueMapper,
  BinaryOperator<U> mergeFunction,
  Supplier<M> mapSupplier)

여기서 mapSupplier결과와 함께 비어있는 새 을 반환하는 함수입니다 .

4.1. List를 ConcurrentMap으로 변경

동일한 예제 를 사용하여 ConcurrentHashMap 을 반환하는 mapSupplier 함수를 추가해 보겠습니다 .

public Map<Integer, Book> listToConcurrentMap(List<Book> books) {
    return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),
      (o1, o2) -> o1, ConcurrentHashMap::new));
}

계속해서 코드를 테스트하겠습니다.

@Test
public void whenCreateConcurrentHashMap() {
    assertTrue(convertToMap.listToConcurrentMap(bookList) instanceof ConcurrentHashMap);
}

4.2. Map 정렬 방법

마지막으로 toMap()은 HashMap을 반환할것이다.. 이를 위해 TreeMapmapSupplier 매개 변수 사용할 것 입니다.

TreeMap 은 기본적으로 키의 자연스러운 순서에 따라 정렬 되기 때문에 직접 을 명시 적으로 정렬 할 필요가 없습니다 .

public TreeMap<String, Book> listToSortedMap(List<Book> books) {
   return books.stream() 
   .collect(
        Collectors.toMap(Book::getName, Function.identity(), (o1, o2) -> o1, TreeMap::new));
   }

따라서 우리의 경우 반환 된 TreeMap 은 책 이름에 따라 알파벳 순서로 정렬됩니다.

@Test
   public void whenMapisSorted() {
   assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals(
   "The Fellowship of the Ring"));
   }

5. 결론

이 기사에서는 Collectors 클래스 toMap ()  메서드를 살펴 보았습니다 . 이를 통해 Stream 에서 을 생성 할 수 있습니다 .

또한 주요 충돌을 해결하고 다양한 맵 구현을 만드는 방법도 배웠습니다.

항상 그렇듯이 코드는 GitHub에서 사용할 수 있습니다 .