简体   繁体   中英

Java 8: merging two Lists containing objects by Id

I have 2 Lists:


// old list
List<Employee> oldList = new ArrayList<>();
Employee emp1 = new Employee();
emp1.setPersonalNumber("123");
emp1.setName("old_name1");
emp1.setStatus(Status.OLD);

Employee emp2 = new Employee();
emp2.setPersonalNumber("456");
emp2.setName("old_name2");
emp2.setStatus(Status.OLD);

oldList.add(emp1);
oldList.add(emp2);

// new list
List<Employee> newList = new ArrayList<>();
Employee newEmp1 = new Employee();
newEmp1.setPersonalNumber("123");
newEmp1.setName("new_name1");
newEmp1.setStatus(Status.NEW);

Employee newEmp2 = new Employee();
newEmp2.setPersonalNumber("456");
newEmp2.setName("new_name2");
newEmp2.setStatus(Status.NEW);

newList.add(newEmp1);
newList.add(newEmp2);

Does anyone know how can I merge those 2 Lists to one List containing all the employees from both lists grouped by "PersonalNumber" and keeping the order of the elemets in newList? newList comes from the Database with a predefined sorting, and I need to keep it that way, so I can't sort it again on the Java side

Result should be:

[
    {"123", "new_name1", NEW},
    {"123", "old_name1", OLD},
    {"456", "new_name2", NEW},
    {"456", "old_name1", OLD},
]

I have the guarantee that both lists have the same size and contain employees with the same personalNumbers. I just want to "inject" each old employee under the new employee with the same personalNumber

You can do like this: As you mentioned that both lists have the same PersonalNumber so you can group by using this property. To ensure order based on the personalNumber , I've used LinkedHashMap .

Stream.concat(newList.stream(), oldList.stream())
            .collect(Collectors.groupingBy(Employee::getPersonalNumber,
                    LinkedHashMap::new, Collectors.toList()))
            .values().stream().flatMap(List::stream)
            .collect(Collectors.toList());

Note: The result of stream#concat is ordered if both of the input streams are ordered.

Collections to sort should work for this.

newList.addAll(oldList);
Collections.sort(newList, Comparator.comparing(Employee::getPersonalNumber) );

The key is that, "This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort."

Since cannot sort the new list, I take that to mean you don't know the order of the new list. You can do it the ol' N^2 method.

for(int i = 0; i<newList.size(); i+=2){
    String newNum = newList.get(i).getPersonalNumber();
    Employee old = oldList.stream().filter(
          emp->newNum.equals(
              emp.getPersonalNumber()
          )
        ).findFirst().orElse(null);
    newList.add(i+1, old);
    oldList.remove(old); //not nescessary?
}

You can do it as follows:

// Sort oldList on personalNumber for faster access when retrieving the records
// with same personalNumber
oldList.sort(Comparator.comparing(Employee::getPersonalNumber));

List<Employee> result = new ArrayList<Employee>();

for (int i = 0; i < newList.size(); i++) {
    // Get an employee, `e` from `newList` and add to `result`
    Employee e = newList.get(i);
    result.add(e);

    // Add elements from `newList` to `result` until a different `personalNumber`
    // occurs
    while (i < newList.size() - 1 && e.getPersonalNumber().equals(newList.get(i + 1).getPersonalNumber())) {
        result.add(newList.get(++i));
    }

    // Iterate `oldList` to find an employee with the `personalNumber` equal to that
    // of `e`
    int j;
    boolean found = false;
    for (j = 0; j < oldList.size(); j++) {
        if (oldList.get(j).getPersonalNumber().equals(e.getPersonalNumber())) {
            found = true;
            break;
        }
    }

    // If `oldList` has an employee with the `personalNumber` equal to that of `e` ,
    // add elements from `oldList` to `result` until a different `personalNumber`
    // occurs. Note that `oldList` has already been sorted.
    if (found) {
        result.add(oldList.get(j));
        while (j < oldList.size() - 1 && oldList.get(++j).getPersonalNumber().equals(e.getPersonalNumber())) {
            result.add(oldList.get(j));
        }
    }
}

Demo:

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

enum Status {
    OLD, NEW;
}

class Employee {
    private String name;
    private String personalNumber;
    private Status status;

    public Employee() {
        super();
    }

    public Employee(String name, String personalNumber, Status status) {
        this.name = name;
        this.personalNumber = personalNumber;
        this.status = status;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPersonalNumber() {
        return personalNumber;
    }

    public void setPersonalNumber(String personalNumber) {
        this.personalNumber = personalNumber;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return personalNumber + ", " + name + ", " + status;
    }
}

public class Main {
    public static void main(String[] args) {
        // old list
        List<Employee> oldList = new ArrayList<Employee>();
        Employee emp1 = new Employee();
        emp1.setPersonalNumber("123");
        emp1.setName("old_name1");
        emp1.setStatus(Status.OLD);

        Employee emp2 = new Employee();
        emp2.setPersonalNumber("456");
        emp2.setName("old_name2");
        emp2.setStatus(Status.OLD);

        oldList.add(emp1);
        oldList.add(emp2);

        // new list
        List<Employee> newList = new ArrayList<>();
        Employee newEmp1 = new Employee();
        newEmp1.setPersonalNumber("123");
        newEmp1.setName("new_name1");
        newEmp1.setStatus(Status.NEW);

        Employee newEmp2 = new Employee();
        newEmp2.setPersonalNumber("456");
        newEmp2.setName("new_name2");
        newEmp2.setStatus(Status.NEW);

        newList.add(newEmp1);
        newList.add(newEmp2);

        // Sort oldList on personalNumber for faster access when retrieving the records
        // with same personalNumber
        oldList.sort(Comparator.comparing(Employee::getPersonalNumber));

        List<Employee> result = new ArrayList<Employee>();

        for (int i = 0; i < newList.size(); i++) {
            // Get an employee, `e` from `newList` and add to `result`
            Employee e = newList.get(i);
            result.add(e);

            // Add elements from `newList` to `result` until a different `personalNumber`
            // occurs
            while (i < newList.size() - 1 && e.getPersonalNumber().equals(newList.get(i + 1).getPersonalNumber())) {
                result.add(newList.get(++i));
            }

            // Iterate `oldList` to find an employee with the `personalNumber` equal to that
            // of `e`
            int j;
            boolean found = false;
            for (j = 0; j < oldList.size(); j++) {
                if (oldList.get(j).getPersonalNumber().equals(e.getPersonalNumber())) {
                    found = true;
                    break;
                }
            }

            // If `oldList` has an employee with the `personalNumber` equal to that of `e` ,
            // add elements from `oldList` to `result` until a different `personalNumber`
            // occurs. Note that `oldList` has already been sorted.
            if (found) {
                result.add(oldList.get(j));
                while (j < oldList.size() - 1 && oldList.get(++j).getPersonalNumber().equals(e.getPersonalNumber())) {
                    result.add(oldList.get(j));
                }
            }
        }

        // Display the result
        result.forEach(System.out::println);
    }
}

Output:

123, new_name1, NEW
123, old_name1, OLD
456, new_name2, NEW
456, old_name2, OLD

Here is another approach with Java streams and InsStream

private List<Employee> mergeLists(List<Employee> oldList, List<Employee> newList) {
    return IntStream.range(0, oldList.size())
            .mapToObj(index -> Arrays.asList(newList.get(index), oldList.get(index)))
            .flatMap(Collection::stream)
            .collect(Collectors.toList());
}

This approach is possible because of

I have the guarantee that both lists have the same size and contain employees with the same personalNumbers.

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