简体   繁体   English

java 8,lambda,对象复制:创建规范化对象的新列表

[英]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.从 REST 服务中,我将得到员工列表的响应。 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.通过创建另一个列表,即List<EmployeeNormalized > ,需要以标准化方式处理上述响应,如下所述。

[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. List<EmployeeNormalized >必须包含唯一的员工对象,并且EmployeeNormalized class 中的List<String>应该包含该员工的所有地址。

How do I create this normalized form of list?如何创建这种标准化形式的列表?

Stream solution: Stream 解决方案:

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.相当复杂,需要调用 distinct 来消除重复的实例。

I would go for simple loop:我会 go 用于简单的循环:

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.基本上,这两种解决方案都使用员工 ID 作为唯一标识符,并使用 map 实例作为 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.如果第一次遇到id,则创建实例并添加地址,如果已经有该id的实例,则获取实例并添加地址。

This task can be resolved with Stream API, providing that there is some wrapper class/record to represent a key (employeeId and employeeName).这个任务可以用 Stream API 来解决,前提是有一些包装类/记录来表示一个(employeeId 和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.即使是普通的ArrayList<Object>也可以用于此目的,但自 Java 16 以来引入record ,最好使用它们。

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 :解决方案本身非常简单:使用Collectors.groupingBy创建一个键,使用Collectors.mapping构建每个员工的地址列表,最后在EmployeeNormalized中加入键( employeeIdemployeeName )和地址值列表:

//
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允许维护类型安全的键而无需额外的转换。 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.可以用一个小的包装器 class 替换记录,以表示一个带有hashCode / equals方法的键,因为此 class 的实例用作中间 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());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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