Javaでリストをマップに変換する方法
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()メソッドを使用して、ListをMapに変換しましょう。
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を使用してAnimalのListを作成します。
@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 Mapのput()メソッドは、最新の付加価値が同じキーで前の付加価値を上書きするように実装されています。
このため、従来の変換と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)));
}
goatがcowを同じ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でから入手できます。