1. 概要

リストからマップへの変換は一般的なタスクです。 このチュートリアルでは、これを行うためのいくつかの方法について説明します。

List の各要素には、結果のMapでキーとして使用される識別子があると想定します。

2. サンプルデータ構造

まず、要素をモデル化します。

public class Animal {
    private int id;
    private String name;

    //  constructor/getters/setters
}

id フィールドは一意であるため、これをキーにすることができます。

従来の方法で変換を始めましょう。

3. Java8より前

明らかに、コアJavaメソッドを使用してリストマップに変換できます。

public Map<Integer, Animal> convertListBeforeJava8(List<Animal> list) {
    Map<Integer, Animal> map = new HashMap<>();
    for (Animal animal : list) {
        map.put(animal.getId(), animal);
    }
    return map;
}

次に、変換をテストします。

@Test
public void whenConvertBeforeJava8_thenReturnMapWithTheSameElements() {
    Map<Integer, Animal> map = convertListService
      .convertListBeforeJava8(list);
    
    assertThat(
      map.values(), 
      containsInAnyOrder(list.toArray()));
}

4. Java8を使用

Java 8以降では、ストリームとコレクターを使用して、リストマップに変換できます。

 public Map<Integer, Animal> convertListAfterJava8(List<Animal> list) {
    Map<Integer, Animal> map = list.stream()
      .collect(Collectors.toMap(Animal::getId, Function.identity()));
    return map;
}

繰り返しますが、変換が正しく行われていることを確認しましょう。

@Test
public void whenConvertAfterJava8_thenReturnMapWithTheSameElements() {
    Map<Integer, Animal> map = convertListService.convertListAfterJava8(list);
    
    assertThat(
      map.values(), 
      containsInAnyOrder(list.toArray()));
}

5. グアバライブラリの使用

コアJavaに加えて、変換にサードパーティのライブラリを使用できます。

5.1. Maven構成

まず、pom.xmlに次の依存関係を追加する必要があります。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

このライブラリの最新バージョンは、常にここにあります。

5.2. Maps.uniqueIndex()を使用した変換

次に、 Maps.uniqueIndex()メソッドを使用して、ListMapに変換しましょう。

public Map<Integer, Animal> convertListWithGuava(List<Animal> list) {
    Map<Integer, Animal> map = Maps
      .uniqueIndex(list, Animal::getId);
    return map;
}

最後に、変換をテストします。

@Test
public void whenConvertWithGuava_thenReturnMapWithTheSameElements() {
    Map<Integer, Animal> map = convertListService
      .convertListWithGuava(list);
    
    assertThat(
      map.values(), 
      containsInAnyOrder(list.toArray()));
}

6. ApacheCommonsLibraryの使用

ApacheCommonsライブラリメソッドを使用して変換を行うこともできます。

6.1. Maven構成

まず、Mavenの依存関係を含めましょう。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

この依存関係の最新バージョンは、こちらで入手できます。

6.2.  MapUtils

次に、 MapUtils.populateMap()を使用して変換を行います。

public Map<Integer, Animal> convertListWithApacheCommons2(List<Animal> list) {
    Map<Integer, Animal> map = new HashMap<>();
    MapUtils.populateMap(map, list, Animal::getId);
    return map;
}

最後に、期待どおりに機能することを確認できます。

@Test
public void whenConvertWithApacheCommons2_thenReturnMapWithTheSameElements() {
    Map<Integer, Animal> map = convertListService
      .convertListWithApacheCommons2(list);
    
    assertThat(
      map.values(), 
      containsInAnyOrder(list.toArray()));
}

7. 価値観の対立

idフィールドが一意でない場合にどうなるかを確認しましょう。

7.1. リスト動物と重複したId

まず、一意でないidを使用してAnimalListを作成します。

@Before
public void init() {

    this.duplicatedIdList = new ArrayList<>();

    Animal cat = new Animal(1, "Cat");
    duplicatedIdList.add(cat);
    Animal dog = new Animal(2, "Dog");
    duplicatedIdList.add(dog);
    Animal pig = new Animal(3, "Pig");
    duplicatedIdList.add(pig);
    Animal cow = new Animal(4, "Cow");
    duplicatedIdList.add(cow);
    Animal goat= new Animal(4, "Goat");
    duplicatedIdList.add(goat);
}

上に示したように、山羊は同じidを持っています。

7.2. 動作の確認

Java Mapput()メソッドは、最新の付加価値が同じキーで前の付加価値を上書きするように実装されています。

このため、従来の変換とApache Commons MapUtils.populateMap()は同じように動作します。

@Test
public void whenConvertBeforeJava8_thenReturnMapWithRewrittenElement() {

    Map<Integer, Animal> map = convertListService
      .convertListBeforeJava8(duplicatedIdList);

    assertThat(map.values(), hasSize(4));
    assertThat(map.values(), hasItem(duplicatedIdList.get(4)));
}

@Test
public void whenConvertWithApacheCommons_thenReturnMapWithRewrittenElement() {

    Map<Integer, Animal> map = convertListService
      .convertListWithApacheCommons(duplicatedIdList);

    assertThat(map.values(), hasSize(4));
    assertThat(map.values(), hasItem(duplicatedIdList.get(4)));
}

goatcowを同じidで上書きしていることがわかります。

ただし、 Collectors.toMap()およびMapUtils.populateMap()は、それぞれIllegalStateExceptionおよびIllegalArgumentExceptionをスローします

@Test(expected = IllegalStateException.class)
public void givenADupIdList_whenConvertAfterJava8_thenException() {

    convertListService.convertListAfterJava8(duplicatedIdList);
}

@Test(expected = IllegalArgumentException.class)
public void givenADupIdList_whenConvertWithGuava_thenException() {

    convertListService.convertListWithGuava(duplicatedIdList);
}

8. 結論

この簡単な記事では、リストマップに変換するさまざまな方法、コアJavaおよびいくつかの一般的なサードパーティライブラリの例を紹介しました。

いつものように、完全なソースコードはGitHubから入手できます。