1. Обзор
Преобразование списка
в карту
— обычная задача. В этом уроке мы рассмотрим несколько способов сделать это.
Предположим, что каждый элемент списка
имеет идентификатор, который будет использоваться в качестве ключа в результирующей карте
.
2. Образец структуры данных
Сначала мы смоделируем элемент:
public class Animal {
private int id;
private String name;
// constructor/getters/setters
}
Поле id
уникально, поэтому мы можем сделать его ключом.
Начнем преобразование традиционным способом.
3. До Java 8
Очевидно, мы можем преобразовать список
в карту
, используя основные методы 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. С Java 8
Начиная с 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()
для преобразования списка
в карту
:
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. Использование библиотеки Apache Commons
Мы также можем выполнить преобразование с помощью метода библиотеки Apache Commons.
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. Список
животных
с повторяющимися идентификаторами
_
Во- первых, мы создаем список
животных с
неуникальными идентификаторами
:
@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. Проверка поведения
Метод put()
в Java Map
реализован таким образом, что последнее добавленное значение перезаписывает предыдущее с тем же ключом. ``
По этой причине традиционное преобразование и 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)));
}
Мы видим, что коза
перезаписывает корову
с тем же идентификатором
.
Однако 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 .