简体   繁体   中英

java 8, lambda , Objects copying: Creating a new list of Normalized objects

From a REST service, I will get the response as List of Employees. Which may contains multiple addresses for same employee as defined below.

[Employee{empId=1, name='Emp1', address='Emp1 Address1'},
Employee{empId=1, name='Emp1', address='Emp1 Address 2'},
Employee{empId=2, name='Emp2', address='Emp2 Address 1'}]

By creating an another list ie List<EmployeeNormalized > , the above response needs to be processed in a normalized way, as defined below.

[EmployeeNormalized{empId=1, name='Emp1',
addresses=[Emp1 Address1, Emp1 Address 2]},
EmployeeNormalized{empId=2, name='Emp2', addresses=[Emp2 Address 1]}]

Code snippet:

class Employee {
    private int empId;
    private String name;
    private String address;

    public Employee(int empId, String name, String address) {
        this.empId = empId;
        this.name = name;
        this.address = address;
    }
   // Setters and Getters
}

class EmployeeNormalized {
    private int empId;
    private String name;
    private List<String> addresses;

    public EmployeeNormalized(int empId, String name, List<String> address) {
        this.empId = empId;
        this.name = name;
        this.addresses = address;
    }
   // Setters and Getters
} 

List<EmployeeNormalized > must contain unique employee objects and List<String> in the EmployeeNormalized class should accommodate all the addresses for that employee.

How do I create this normalized form of list?

Stream solution:

public class Normalize {

    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee(1, "Emp1", "Address 1"));
        employees.add(new Employee(1, "Emp1", "Address 2"));
        employees.add(new Employee(2, "Emp2", "Address 3"));
        List<EmployeeNormalized> employeeNormalizedList = employees.stream()
                .map(new Function<Employee, EmployeeNormalized>() {

                    private final Map<Integer, EmployeeNormalized> employeeIdMap = new HashMap<>();

                    @Override
                    public EmployeeNormalized apply(Employee employee) {
                        EmployeeNormalized normalized = this.employeeIdMap.computeIfAbsent(employee.getEmpId(),
                                key -> new EmployeeNormalized(employee.getEmpId(), employee.getName(), new ArrayList<>()));
                        normalized.getAddresses().add(employee.getAddress());
                        return normalized;
                    }
                })
                .distinct()
                .collect(Collectors.toList());
        employeeNormalizedList.forEach(System.out::println);
    }
}

Quite complex, need to call distinct to get rid of duplicating instances.

I would go for simple loop:

public class Normalize {

    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee(1, "Emp1", "Address 1"));
        employees.add(new Employee(1, "Emp1", "Address 2"));
        employees.add(new Employee(2, "Emp2", "Address 3"));

        Map<Integer, EmployeeNormalized> employeeIdMap = new HashMap<>();
        for (Employee employee : employees) {
            EmployeeNormalized normalized = employeeIdMap.computeIfAbsent(employee.getEmpId(), key -> new EmployeeNormalized(employee.getEmpId(), employee.getName(), new ArrayList<>()));
            normalized.getAddresses().add(employee.getAddress());
        }
        List<EmployeeNormalized> employeeNormalizedList = new ArrayList<>(employeeIdMap.values());
        employeeNormalizedList.forEach(System.out::println);
    }
}

Basically both solutions use employee id as unique identifer, and map instances to id. If id is met for the first time, create instance and add address, if there is already instances for this id, get the instance and add address.

This task can be resolved with Stream API, providing that there is some wrapper class/record to represent a key (employeeId and employeeName). Even plain ArrayList<Object> could be used for this purpose but with introduction of record since Java 16 it's better to use them.

The solution itself is pretty straightforward: use Collectors.groupingBy to create a key, Collectors.mapping to build a list of addresses per employee, and finally join the key ( employeeId and employeeName ) and the value list of addresses in the EmployeeNormalized :

//
List<Employee> employees = .... ; // build employee list
List<EmployeeNormalized> normEmployees = employees
    .stream()
    .collect(Collectors.groupingBy(
        emp -> Arrays.asList(emp.getEmpId(), emp.getName()),
        LinkedHashMap::new, // maintain insertion order
        Collectors.mapping(Employee::getAddress, Collectors.toList())
    )) // Map<List<Object>, List<String>>
    .entrySet()
    .stream()
    .map(e -> new EmployeeNormalized(
        ((Integer) e.getKey().get(0)).intValue(), // empId
        (String) e.getKey().get(1),               // name
        e.getValue()
    ))
    .collect(Collectors.toList());

Use of record allows to maintain typesafe keys without extra casting. Record can be replaced with a small wrapper class to represent a key with hashCode / equals methods overridden because the instances of this class are used as keys in an intermediate map.

// Java 16+
record EmpKey(int empId, String name) {}

List<EmployeeNormalized> normEmployees = employees
    .stream()
    .collect(Collectors.groupingBy(
        emp -> new EmpKey(emp.getEmpId(), emp.getName()),
        LinkedHashMap::new, // maintain insertion order
        Collectors.mapping(Employee::getAddress, Collectors.toList())
    )) // Map<List<Object>, List<String>>
    .entrySet()
    .stream()
    .map(e -> new EmployeeNormalized(
        e.getKey().empId(), e.getKey().name(), e.getValue()
    ))
    .collect(Collectors.toList());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM