Post

Java List를 Map으로 변환

1. 샘플 데이터 구조

요소를 모델링한다.

1
2
3
4
5
6
public class Animal {
    private int id;
    private String name;

    //  constructor/getters/setters
}

id 필드는 고유하므로 키로 만들 수 있다.

전통적인 방법으로 변환을 시작한다.

2. 자바 8 이전

핵심 Java 메서드를 사용하여 List를 Map으로 변환할 수 있다.

1
2
3
4
5
6
7
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;
}

변환을 테스트한다.

1
2
3
4
5
6
7
8
9
@Test
public void givenAList_whenConvertBeforeJava8_thenReturnMapWithTheSameElements() {
    Map<Integer, Animal> map = convertListService
      .convertListBeforeJava8(list);
    
    assertThat(
      map.values(), 
      containsInAnyOrder(list.toArray()));
}

3. 자바 8 사용

Java 8부터 Streams와 Collectors를 사용하여 List를 Map으로 변환할 수 있다.

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

변환이 올바르게 수행되었는지 확인한다.

1
2
3
4
5
6
7
8
@Test
public void givenAList_whenConvertAfterJava8_thenReturnMapWithTheSameElements() {
    Map<Integer, Animal> map = convertListService.convertListAfterJava8(list);
    
    assertThat(
      map.values(), 
      containsInAnyOrder(list.toArray()));
}

4. 구아바 라이브러리 사용

핵심 Java 외에도 변환을 위해 타사 라이브러리를 사용할 수 있다.

1) 메이븐 구성

pom.xml에 다음 종속성을 추가한다.

1
2
3
4
5
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

2) Maps.uniqueIndex()를 사용하여 변환

Maps.uniqueIndex() 메서드를 사용하여 List를 Map으로 변환한다.

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

변환을 테스트한다.

1
2
3
4
5
6
7
8
9
@Test
public void givenAList_whenConvertWithGuava_thenReturnMapWithTheSameElements() {
    Map<Integer, Animal> map = convertListService
      .convertListWithGuava(list);
    
    assertThat(
      map.values(), 
      containsInAnyOrder(list.toArray()));
}

5. Apache Commons Library 사용

Apache Commons Library 방법으로 변환할 수도 있다.

1) 메이븐 구성

Maven 종속성을 추가한다.

1
2
3
4
5
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

2) MapUtils

MapUtils.populateMap()을 사용하여 변환을 수행한다.

1
2
3
4
5
public Map<Integer, Animal> convertListWithApacheCommons(List<Animal> list) {
    Map<Integer, Animal> map = new HashMap<>();
    MapUtils.populateMap(map, list, Animal::getId);
    return map;
}

변환을 테스트한다.

1
2
3
4
5
6
7
8
9
@Test
public void givenAList_whenConvertWithApacheCommons_thenReturnMapWithTheSameElements() {
    Map<Integer, Animal> map = convertListService
      .convertListWithApacheCommons(list);
    
    assertThat(
      map.values(), 
      containsInAnyOrder(list.toArray()));
}

7. Values 충돌

id 필드가 고유하지 않은 경우 어떻게 되는지 확인한다.

1) ID가 중복 된 동물 목록

고유하지 않은 ID를 가진 Animal 목록을 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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);
}

CowGoat는 같은 id를 가진다.

2) 확인

Java Map의 put() 메서드는 가장 최근에 추가된 값이 이전 값을 동일한 키로 덮어쓰도록 구현된다.

이러한 이유로 기존 변환과 Apache Commons MapUtils.populateMap()은 동일한 방식으로 작동한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void givenADupIdList_whenConvertBeforeJava8_thenReturnMapWithRewrittenElement() {

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

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

@Test
public void givenADupIdList_whenConvertWithApacheCommons_thenReturnMapWithRewrittenElement() {

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

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

Goat가 같은 id를 가진 Cow를 덮어쓰는 것을 볼 수 있다.

그러나 Collectors.toMap()MapUtils.populateMap()은 각각 IllegalStateExceptionIllegalArgumentException을 발생시킨다.

1
2
3
4
5
6
7
8
9
10
11
@Test(expected = IllegalStateException.class)
public void givenADupIdList_whenConvertAfterJava8_thenException() {

    convertListService.convertListAfterJava8(duplicatedIdList);
}

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

    convertListService.convertListWithGuava(duplicatedIdList);
}

[출처 및 참고]

This post is licensed under CC BY 4.0 by the author.