在2个数组列表中获取匹配和不匹配对象的最有效方法



我有一个任务,读取2个文件并匹配文件的内容,并提供两个文件的不匹配条目的列表。这意味着我必须给出两个文件中有多少匹配条目,文件1中有多少不匹配条目不在文件2中,文件2中有多少不匹配条目不在文件1中。

我的方法是读取文件,从中创建java对象,将2个文件的内容放入2个单独的数组列表并进行比较。下面列出了我当前的代码。为了澄清,我想检查对象的内容(例如:检查两个文件中的EmployeeID和match)。

在下面的代码中,我将file1的内容与file2匹配,并从file2中删除匹配的内容。可以很好地匹配条目,并获得file1与file2的不匹配计数。

我计划匹配file2中的剩余项目,并使用fileTwoEmpList作为第一个参数,fileOneEmpList作为第二个参数,在相同的compareByEmpIdandDOB方法中进行另一轮,获得与file1相比file2的不匹配计数。但我觉得这有点小题大做,而且效率不高。有谁能指出不同的方法吗?

两个数组列表都已排序。提前感谢!

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;
}
}

对于这个任务,您不需要对这些列表进行排序。

根据集合论,你需要找到集合差。即查找只出现在第一个或第二个列表中的所有唯一对象。

这个任务可以在几行代码中解决,并且具有线性时间复杂度。但在EmployeeDetails环境下实施equals/hashCode合同是非常重要的。

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+
}

基于流的解决方案的时间复杂度也是线性的。但正如我所说,基于Collections API的第一种解决方案更简单,性能也稍好一些。

如果由于某种原因,EmployeeDetails中没有正确实现equals()hashCode()。你无法控制这个类,也无法改变它。然后,您可以声明一个包装器类并执行相同的操作。

下面是如何使用Java 16记录创建包装器的示例。方法编译器在empIdempDob的基础上生成equals()hashCode()

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

基于empIDempDOBEmployeeDetails类的equals/hashCode的实现可能看起来像这样(也可以,您可以使用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);
}

最新更新