1. 개요
이 빠른 자습서에서는 Collectors 클래스 의 toMap () 메서드에 대해 설명합니다 . 이를 사용하여 Stream을 Map 인스턴스 로 수집 합니다.
여기에서 다루는 모든 예제에 대해 책 목록을 시작점으로 사용하고이를 다른 Map 구현 으로 변환합니다 .
2. List 에 를 Map으로 변경
List 를 Map 으로 변환하여 가장 간단한 경우부터 시작하겠습니다 .
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을 반환할것이다.. 이를 위해 TreeMap 을 mapSupplier 매개 변수 로 사용할 것 입니다.
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 에서 새 맵 을 생성 할 수 있습니다 .
또한 주요 충돌을 해결하고 다양한 맵 구현을 만드는 방법도 배웠습니다.