Самый эффективный способ получить совпадающие и несопоставленные объекты в 2 списках массивов

У меня есть задача прочитать 2 файла и сопоставить содержимое файлов и предоставить список несопоставленных записей обоих файлов. Это означает, что я должен представить, сколько совпадающих записей в двух файлах и сколько несопоставленных записей в файле 1, которых нет в файле 2, сколько несопоставленных записей в файле 2, которых нет в файле 1.

Мой apporach читает файлы, создает из них java-объекты, помещает содержимое 2 файлов в 2 отдельных массива и сравнивает их. Мой текущий код указан ниже. Для уточнения я хочу проверить содержимое объекта (например: проверить EmployeeID и сопоставить оба файла).

В приведенном ниже коде я сопоставил содержимое файла 1 с файлом 2 и удалил совпавшее содержимое из файла 2. Отлично работает, чтобы сопоставить записи и получить непревзойденное количество файлов 1 по сравнению с файлом 2.

Я планирую сопоставить оставшиеся элементы в файле 2 и пройти еще один раунд тем же compareByEmpIdandDOBметодом, используя fileTwoEmpList в качестве первого параметра и fileOneEmpList в качестве второго параметра, чтобы получить количество несопоставленных файлов в файле 2 по сравнению с файлом 1. Но я считаю, что это перебор и не очень эффективно. Может ли кто-нибудь указать другой подход, если есть какие-либо проблемы?

Оба массива отсортированы. Заранее спасибо !

public class EmpMatching {

    public void compareLists(List<EmployeeDetails> fileOneEmpList, List<EmployeeDetails> fileTwoEmpList){

        Collections.sort(fileOneEmpList);
        Collections.sort(fileTwoEmpList);

        List<EmployeeDetails> unmatchedFromListTwo = compareByEmpIdandDOB(fileOneEmpList,fileTwoEmpList);

    }

    public List<EmployeeDetails>  compareByEmpIdandDOB(List<EmployeeDetails> fileOneEmpList,List<EmployeeDetails> fileTwoEmpList){

        int matchEmpCountFromTwoFiles = 0;
        System.out.println("File One List Size Before Recon " + fileTwoEmpList.size());

        for(EmployeeDetails fileOneEmp : fileOneEmpList){

            for(int index = 0;index < fileTwoEmpList.size();index++ ){

                EmployeeDetails fileTwoEmp= fileTwoEmpList.get(index);

                if(fileOneEmp.getEmpID().equals(fileTwoEmp.getEmpID()) && fileOneEmp.getEmpDOB().equals(fileTwoEmp.getEmpDOB())){
                    matchEmpCountFromTwoFiles++;
                    fileTwoEmpList.remove(fileTwoEmp);

                    System.out.println("Match Found " + fileOneEmp.getEmpID());
                }
            }

            System.out.println("File Two List Size " + fileTwoEmpList.size());
        }

        System.out.println("Match Count >>>>>  " + matchEmpCountFromTwoFiles);
        System.out.println("File Two List Size >>>>> " + fileTwoEmpList.size());

        return fileTwoEmpList;

    }
}


//Model class

public class EmployeeDetails implements Comparable<EmployeeDetails>{


    private String EmpID;

    private String EmpName;

    private String EmpDOB;

    @Override
    public int compareTo(EmployeeDetails o) {
        return 0;
    }
}

Метод compareTo вернет 0 для любой пары объектов EmployeeDetails, вы не можете сортировать эти списки на основе этой реализации заглушки Comparable. И нет смысла сортировать эти данные, если вам нужно просто получить уникальные объекты из обоих списков.

Alexander Ivanchenko 23.04.2022 10:40

Реализует ли EmployeeDetails equals() и hashCode() на основе empId и empDob? Или, если это не так, вы можете изменить реализацию?

Alexander Ivanchenko 23.04.2022 10:42
Основы программирования на Java
Основы программирования на Java
Java - это высокоуровневый объектно-ориентированный язык программирования, основанный на классах.
Концепции JavaScript, которые вы должны знать как JS программист!
Концепции JavaScript, которые вы должны знать как JS программист!
JavaScript (Js) - это язык программирования, объединяющий HTML и CSS с одной из основных технологий Всемирной паутины. Более 97% веб-сайтов используют...
0
2
38
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам не нужно сортировать эти списки для этой задачи.

Что касается Теория множеств, вам нужно найти установить разницу. т.е. чтобы найти все уникальные объекты, которые появляются только в первом или во втором списке.

Эта задача может быть решена в несколько строк кода с линейной временной сложностью. Но важно реализовать контракт equals/hashCode в EmployeeDetails.

public List<EmployeeDetails> compareLists(List<EmployeeDetails> fileOneEmpList,
                                          List<EmployeeDetails> fileTwoEmpList) {
    
    Set<EmployeeDetails> emp1 = new HashSet<>(fileOneEmpList);
    Set<EmployeeDetails> emp2 = new HashSet<>(fileTwoEmpList);
    
    emp1.removeAll(emp2); 
    emp2.removeAll(emp1);
    emp1.addAll(emp2);

    return new ArrayList<>(emp1);
}

Описанный выше подход является одновременно и самым эффективным, и самым простым.

Если вам удобно работать с Streams API, вы можете попробовать другой подход и реализовать этот метод следующим образом:

public List<EmployeeDetails> compareLists(List<EmployeeDetails> fileOneEmpList,
                                          List<EmployeeDetails> fileTwoEmpList) {
    
    return Stream.of(new HashSet<>(fileOneEmpList), new HashSet<>(fileTwoEmpList)) // wrapping with sets to ensure uniqueness (if objects in the list are guaranteed to be unique - use lists instead) 
        .flatMap(Collection::stream)
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
        .entrySet().stream()
        .filter(entry -> entry.getValue() == 1) // i.e. object appear only once either in the first or in the second list
        .map(Map.Entry::getKey)
        .collect(Collectors.toList()); // .toList(); for Java 16+
}

Временная сложность решения на основе потока также будет линейной. Но, как я уже сказал, первое решение, основанное на API коллекций, проще и немного производительнее.

Если по какой-то причине нет правильной реализации equals() и hashCode() в файле EmployeeDetails. И вы не имеете никакого контроля над этим классом и не можете его изменить. Затем вы можете объявить класс-оболочку и выполнить те же действия.

Ниже приведен пример создания оболочки с использованием записей Java 16. Методы equals() и hashCode() будут сгенерированы компилятором на основе empId и empDob.

public record EmployeeWrapper(String empId, String empDob) {
    public EmployeeWrapper(EmployeeDetails details) {
        this(details.getEmpID(), details.empDOB);
    }
}

Реализация equals/hashCode для класса EmployeeDetails на основе empID и empDOB может выглядеть так (Кроме того, вы можете использовать средства вашей IDE для создания этих методов.):

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        
        EmployeeDetails that = (EmployeeDetails) o;            
        return empID.equals(that.empID) && empDOB.equals(that.empDOB);
    }

    @Override
    public int hashCode() {
        return Objects.hash(empID, empDOB);
    }

Спасибо за ответ @Alexander .. Мне нужно использовать Java8. Я довольно новичок, когда дело доходит до концепции хэш-кода и равенства. Я должен реализовать оба, чтобы быть уверенным, что моя реализация будет точной?

Ran_Macavity 23.04.2022 11:20

@Ran_Macavity Да, помните, что если ваш класс предназначен для использования с коллекциями, он должен иметь правильную реализацию контракта equeals/hashCode. И ваша IDE может генерировать эти методы (alt + enter в Intellij), вам просто нужно просмотреть правильные атрибуты. Я добавил реализации в самый низ ответа.

Alexander Ivanchenko 23.04.2022 11:35

Другие вопросы по теме