[Java] Convert 1-to-N List to Map

Introduction

For example, when you want to process the result extracted by joining two parent and child tables from DB for each key. You can process it as a List, but it looks better if you group it by key, right? (I personally like it because it improves readability. If there is a performance problem, I can't help it.) This time, I will try to convert to Map (key: parent table, value: child table) after batch acquisition with side-by-side List.

Premise

ER

Department master

--Department id

Employee master

--Employee id --Employee name --Department id (FK)

If you get this by inner join, it will be like this. ↓

select_all.sql


select *
from Department Master
inner join employee master using department id;

Image of acquisition result (List ) ↓

Department Employee
Department 1 Employee 1
Department 1 Employee 2
Department 2 Employee 3
Department 2 Employee 4
Department 2

The purpose of this time is to convert this into "Map <department, List >" with the department as the key and the employee as the value.

Implementation

environment

What I made

--DemoService: Main process --DbMapper: Actually an OR mapper such as Mybatis. --DbMapperImpl: DbMapper implemented for testing. --Result: Store the result of select_all.sql --Department: Department table --Employee: Employee table

Service class

For the time being, I created two list2Map7 methods of ** Java7 ** version and ** Java8 (Stream API) **. The results should be the same for both. After all, the implementation with Stream API has a small amount of code and is easy to see. I don't really want to implement anything that has nothing to do with business logic. If you do your best, list2Map8 may be easier to see. But it seems that there will be more unreadable code besides the Service class.

DemoService.java


package com.example.demo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class DemoService {

	@Autowired
	DbMapper mapper;

	public void execute(String[] args) {
		log.info("### START ###");
		// select from DB
		List<Result> list = mapper.select();
		//Extracted contents
		list.forEach(System.out::println);
		// List ⇒ LinkedHashMap
		Map<Department, List<Employee>> map = list2Map8(list);
		System.out.println();
		//Output conversion result
		for (Map.Entry<Department, List<Employee>> entry : map.entrySet()) {
			System.out.println("Key:" + entry.getKey());
			entry.getValue().forEach(e -> System.out.println("    Value:" + e));
		}
		log.info("### END ###");
	}

	/**
	 *List⇒Map conversion(java7Ver).
	 * 
	 * @param list
	 * @return
	 */
	private Map<Department, List<Employee>> list2Map7(List<Result> list) {
		if (list.isEmpty()) {
			return Collections.emptyMap();
		}
		Map<Department, List<Employee>> map = new LinkedHashMap<>();
		//Last key
		Department prevDep = null;
		List<Employee> tempList = null;
		for (Result result : list) {
			Department dep = result.getDepartment();
			//If the key changes
			if (!dep.equals(prevDep)) {
				//Initialize list of values
				tempList = new ArrayList<>();
				//Added to the map with the values initialized(Set reference to map)
				map.put(dep, tempList);
				//Use this key as the previous key
				prevDep = dep;
			}
			//Add value to tempList that references map
			tempList.add(result.getEmployee());
		}
		return map;
	}

	/**
	 *List⇒Map conversion(java8Ver).
	 * 
	 * @param list
	 * @return
	 */
	private Map<Department, List<Employee>> list2Map8(List<Result> list) {
		// List(value)⇒Map(キー、value)Conversion to
		Map<Department, List<Employee>> ret = list.stream()
				.collect(Collectors.groupingBy(Result::getDepartment,
				LinkedHashMap::new, Collectors.mapping(Result::getEmployee, Collectors.toList())));
		return ret;
	}
}

I am using ** LinkedHashMap ** to keep the acquisition order. It is not retained in HashMap. (Thank you for your comment and improved the conversion method.)

DbMapper interface

Actually, it feels like using Mybatis or OR mapper.

DbMapper.java


package com.example.demo;

import java.util.List;
import org.springframework.stereotype.Component;

@Component
public interface DbMapper {
	List<Result> select();
}

DbMapperImpl class

Returns debug data.

DbMapperImpl.java


package com.example.demo;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Component;

@Component
public class DbMapperImpl implements DbMapper {
	@Override
	public List<Result> select() {
		List<Result> list = new ArrayList<>();
		list.add(getResult(1, 101));
		list.add(getResult(1, 102));
		list.add(getResult(2, 203));
		list.add(getResult(3, 304));
		list.add(getResult(3, 305));
		list.add(getResult(3, 306));
		return list;
	}

	private Result getResult(int did, int eid) {
		Department department = new Department();
		department.setDepartmentId(did);
		department.setDepartmentName("system" + did + "Division");

		Employee employee = new Employee();
		employee.setEmployeeId(eid);
		employee.setName("Yamada" + eid + "Ro");
		employee.setDepartmentId(department.getDepartmentId());

		Result result = new Result(department, employee);
		return result;
	}
}

Result class

The object where the SELECT result from the DB is stored first.

Result.java


package com.example.demo;

import lombok.Data;

@Data
public class Result {
	private Department department;
	private Employee employee;

	public Result() {
	}

	public Result(Department department, Employee employee) {
		this.department = department;
		this.employee = employee;
	}
}

Department class

Department master. Lombok @Data overrides hashcode and equals. The same applies to the employee master. Depending on the situation, if this actually represents one record in the table, then the only field to override is PK. In that case, create your own without using @Data. Override is required because LinkedHashMap (HashMap) is used.

Department.java


package com.example.demo;

import lombok.Data;

@Data
public class Department {
	private Integer departmentId;
	private String departmentName;
}

Employee class

Employee master.

Employee.java


package com.example.demo;

import lombok.Data;

@Data
public class Employee {
	private Integer employeeId;
	private String name;
	private Integer departmentId;
}

Execution result

It has been properly converted to a map for each key (department).

Execution result


Result(department=Department(departmentId=1, departmentName=System Section 1), employee=Employee(employeeId=101, name=Yamada 101ro, departmentId=1))
Result(department=Department(departmentId=1, departmentName=System Section 1), employee=Employee(employeeId=102, name=102ro Yamada, departmentId=1))
Result(department=Department(departmentId=2, departmentName=System Section 2), employee=Employee(employeeId=203, name=Yamada 203ro, departmentId=2))
Result(department=Department(departmentId=3, departmentName=System Section 3), employee=Employee(employeeId=304, name=Yamada 304ro, departmentId=3))
Result(department=Department(departmentId=3, departmentName=System Section 3), employee=Employee(employeeId=305, name=Yamada 305ro, departmentId=3))
Result(department=Department(departmentId=3, departmentName=System Section 3), employee=Employee(employeeId=306, name=Yamada 306ro, departmentId=3))

Key:Department(departmentId=1, departmentName=System Section 1)
    Value:Employee(employeeId=101, name=Yamada 101ro, departmentId=1)
    Value:Employee(employeeId=102, name=102ro Yamada, departmentId=1)
Key:Department(departmentId=2, departmentName=System Section 2)
    Value:Employee(employeeId=203, name=Yamada 203ro, departmentId=2)
Key:Department(departmentId=3, departmentName=System Section 3)
    Value:Employee(employeeId=304, name=Yamada 304ro, departmentId=3)
    Value:Employee(employeeId=305, name=Yamada 305ro, departmentId=3)
    Value:Employee(employeeId=306, name=Yamada 306ro, departmentId=3)

Afterword

Lombok is convenient, isn't it?

Recommended Posts

[Java] Convert 1-to-N List to Map
For Java beginners: List, Map, Iterator / Array ... How to convert?
[Java] [ibatis] How to get records of 1-to-N relationship with List <Map <>>
[Java] How to use Map
[Java] How to use Map
Convert Java Powerpoint to XPS
How to use Java Map
How to convert Java radix
[Java] Convert ArrayList to array
Sample code to convert List to List <String> in Java Stream
I learned stream (I want to convert List to Map <Integer, List>)
Convert Map <K, V1> to Map <K, V2> (Convert Map Value)
JAVA (Map)
[Java] How to use List [ArrayList]
[Android] Convert Android Java code to Kotlin
[Java] Conversion from array to List
Java8 list conversion with Stream map
[Java] Convert array to ArrayList * Caution
[Android] Convert Map to JSON using GSON in Kotlin and Java
[Java] Get List / Map elements with Iterator
[Java] How to add data to List (add, addAll)
Convert all Android apps (Java) to Kotlin
Convert from java UTC time to JST time
[Java] Convert Object type null to String type
Change List <Optional <T >> to Optional <List <T >> in Java
Convert SVG files to PNG files in Java
[Java] Map # merge is hard to understand.
List, Set, Map
Java memorandum (list)
Clone Java List.
Array / list / map
[Java] Map comparison
[Java] Introduction to Java
Introduction to java
About List [Java]
[Java] From two Lists to one array list
[Java] Convert DB code to code value using enum
Convert Json to List <T> as it is
Convert Java nested beans to aaa.bbb [0] .ccc format
[Java] How to operate List using Stream API
[java] sort in list
Launch Docker from Java to convert Office documents to PDF
<java> Read Zip file and convert directly to string
[Java] Convert PDF version
Mastering Kotlin ~ Convert Java File to Kotlin File Road to Graduation ~ Part 3
Convert Java enum enums and JSON to and from Jackson
List processing to understand with pictures --java8 stream / javaslang-
Changes from Java 8 to Java 11
Sum from Java_1 to 100
How to use Map
java: How to write a generic type list [Note]
[Java] Convert character strings to uppercase / lowercase (AOJ⑨-swap uppercase and lowercase)
[Java] Connect to MySQL
About Java Array List
Kotlin's improvements to Java
Java applications convert Word (DOC / DOCX) documents to PDF
From Java to Ruby !!
Java bidirectional map library
Mastering Kotlin ~ Convert Java File to Kotlin File Road to Graduation ~ Part 1
How to convert A to a and a to A using AND and OR in Java
How to convert a file to a byte array in Java