提问人:Sachintha Hewawasam 提问时间:4/11/2023 最后编辑:Mark RotteveelSachintha Hewawasam 更新时间:4/12/2023 访问量:73
高效地比较两个大型 Java 列表以查找唯一项目
Efficiently comparing two large Java lists to find unique items
问:
如何有效地比较 Java 中的两个大型对象列表,并识别一个列表中存在而另一个列表中不存在的项目?
例:
假设我有两个大型 CSV 文件,其中包含数千名员工的数据,其中包含姓名、部门和工资列。我需要比较这两个文件,并根据他们的姓名和部门来识别一个文件中存在但另一个文件中不存在的任何员工。
public static void compareCSVFiles(String file1, String file2) {
List<Employee> list1 = readCSVFile(file1);
List<Employee> list2 = readCSVFile(file2);
List<Employee> uniqueTo1 = new ArrayList<>();
List<Employee> uniqueTo2 = new ArrayList<>();
for (Employee emp1 : list1) {
boolean found = false;
for (Employee emp2 : list2) {
if (emp1.getName().equals(emp2.getName()) && emp1.getDepartment().equals(emp2.getDepartment())) {
found = true;
break;
}
}
if (!found) {
uniqueTo1.add(emp1);
}
}
for (Employee emp2 : list2) {
boolean found = false;
for (Employee emp1 : list1) {
if (emp2.getName().equals(emp1.getName()) && emp2.getDepartment().equals(emp1.getDepartment())) {
found = true;
break;
}
}
if (!found) {
uniqueTo2.add(emp2);
}
}
System.out.println("Employees unique to " + file1 + ":");
for (Employee emp : uniqueTo1) {
System.out.println(emp.getName() + " (" + emp.getDepartment() + ")");
}
System.out.println("Employees unique to " + file2 + ":");
for (Employee emp : uniqueTo2) {
System.out.println(emp.getName() + " (" + emp.getDepartment() + ")");
}
}
- 该代码逐行读取 CSV 文件,并将每一行存储为字符串。对于大型文件,这可能不节省内存或可伸缩。
- 代码使用嵌套循环将一个列表中的每个员工与另一个列表中的每个员工进行比较,这对于大文件来说可能很慢且效率低下。
- 该代码仅标识一个列表唯一的员工,而不标识另一个列表唯一的员工。它不会识别两个列表中都存在的员工。
我认为我们可以更有效地编写这些代码。我想知道你对此的看法。
答:
0赞
raiton
4/11/2023
#1
不要使用列表,而是使用具有唯一标识符的地图(例如员工 ID) 然后运行第二个列表/映射以查看第一个映射是否包含它。
仅此一项就可以为您节省大量的复杂性/时间
0赞
khachik
4/11/2023
#2
您可以将它们加载到集合中(根据相等标准实现哈希码/等号)并相交/差异。如果内容适合内存,这将起作用。
如果需要可扩展的解决方案,可以在磁盘上对它们进行排序、合并、排序和逐行扫描。
最后,如果您想要一个真正可扩展的解决方案,那么我们 Spark。
0赞
WJS
4/12/2023
#3
这是一种方式。开销并没有那么糟糕,因为删除工作是由散列的集合完成的。
List<Employee> list1 = List.of(
new Employee("John", "Fiance"),
new Employee("Mary", "Engineering"),
new Employee("John", "Engineering"),
new Employee("Linda", "Engineering"),
new Employee("Alice", "Personel"));
List<Employee> list2 = List.of(
new Employee("John", "Personel"),
new Employee("Mary", "Fiance"),
new Employee("John", "Engineering"),
new Employee("Linda", "Engineering"),
new Employee("Alice", "Personel"));
Set<Employee> uniqueTo1 = new HashSet<>(list1);
Set<Employee> uniqueTo2 = new HashSet<>(list2);
uniqueTo1.removeAll(list2);
uniqueTo2.removeAll(list1);
uniqueTo1.forEach(System.out::println);
System.out.println();
uniqueTo2.forEach(System.out::println);
指纹
Employee[getName=Mary, getDepartment=Engineering]
Employee[getName=John, getDepartment=Fiance]
Employee[getName=Mary, getDepartment=Fiance]
Employee[getName=John, getDepartment=Personel]
笔记:
我在这里使用了一条记录来促进演示。 并且必须在 Employee 类中重写才能正常工作。这也允许您比较对象而不是字段。
Equals
hashCode
如果允许重复项,则必须使用列表,因为集不允许重复项。
最好先阅读至少一个列表,然后在阅读第二个列表时创建排除列表。这有助于避免列表重复。但是,由于您的列表似乎没有那么大,这应该不是问题。
这是如何做到的。假设已读入。list1
Employee emp = null;
List<Employee> list2 = new ArrayList<>();
List<Employee> uniqueTo1 = list1; // could also make a copy
while (reading next of what would be list2) {
list2.add(emp);
if (!list1.contains(emp)) {
uniqueTo2.add(emp); // if not in list1, must be unique to list2
else {
list1.remove(emp); // if it is in list1 it can't be unique so remove it.
} // list1 is now becoming unique to list1.
}
评论
HashSet
Map
O(1)
O(n)
O(n²)
HashSet
removeAll()