java中两个集合之间的公共元素
我试图在2个元素为Object类型的列表之间找到公共元素 比如说java中两个集合之间的公共元素,java,functional-programming,Java,Functional Programming,我试图在2个元素为Object类型的列表之间找到公共元素 比如说 class Student { String firstName; String lastName; float gpa; ... other attributes } 同样地 class Employee{ String firstName; String lastName; float salary; ... other attributes } 我需要找出与学生同名的员
class Student {
String firstName;
String lastName;
float gpa;
... other attributes
}
同样地
class Employee{
String firstName;
String lastName;
float salary;
... other attributes
}
我需要找出与学生同名的员工的条目
我做了以下几件事
Set<String> studentFirstNames = studentList.stream().
map(student-> student.getFirstName()).
collect(Collectors.toSet());
List<Employee> employeeAndStudents = employeeList.stream()
.filter(employee-> studentFirstNames.contains(employee.getFirstName())
.collect(Collectors.toList());
Set studentFirstNames=studentList.stream()。
映射(student->student.getFirstName()。
collect(Collectors.toSet());
List employeeAndStudents=employeeList.stream()
.filter(employee->studentfirstname.contains(employee.getFirstName())
.collect(Collectors.toList());
这是可行的,但我正试图找到一个解决方案,使公共属性不限于单个字段(例如,名字和姓氏)
列表可以包含许多元素,但不管大小,我想知道是否存在使用FP的更好解决方案。您可能希望创建一个自定义“均衡器”类,该类从要检查的任一类型中获取一个对象,并委托给另一个类的字段
class Equalizer {
private final String firstName;
private final String lastName;
Equalizer(Student student){
this.firstName = student.getFirstName;
this.lastName = student.getLastName;
}
Equalizer(Employee employee){
this.firstName = employee.getFirstName;
this.lastName = employee.getLastName;
}
// have equals() and hashcode() implemented by your IDE based on the fields.
}
然后将第一个集合转换为均衡器
对象
Set<Equalizer> equalizers = studentList.stream()
.map(student-> new Equalizer(student))
.collect(Collectors.toSet());
Set equalizers=studentList.stream()
.map(学生->新均衡器(学生))
.collect(收集器.toSet());
然后你可以对它们进行比较:
List<Employee> employeeAndStudents = employeeList.stream()
.filter(employee-> equalizers.contains(new Equalizer(employee))
.collect(Collectors.toList());
List employeeAndStudents=employeeList.stream()
.filter(员工->均衡器.contains(新均衡器(员工))
.collect(Collectors.toList());
使用两个哈希函数怎么样
Function<Student,Integer> studentHashFunction = s -> Objects.hash(s.getFirstName(), s.getLastName());
Function<Employee,Integer> employeeHashFunction = e -> Objects.hash(e.getFirstName(), e.getLastName());
Set<Integer> studentHash = studentList
.stream()
.map(studentHashFunction)
.collect(Collectors.toSet());
List<Employee> employeeAndStudents = employeeList
.stream()
.filter(employee-> studentHash.contains(employeeHashFunction.apply(employee)))
.collect(Collectors.toList());
Function studentHashFunction=s->Objects.hash(s.getFirstName(),s.getLastName());
函数employeeHashFunction=e->Objects.hash(e.getFirstName(),e.getLastName());
Set studentHash=studentList
.stream()
.map(studentHashFunction)
.collect(收集器.toSet());
List employeeAndStudents=employeeList
.stream()
.filter(employee->studentHash.contains(employeeHashFunction.apply(employee)))
.collect(Collectors.toList());
如果您不想添加任何新类,并且可以灵活地修改它们(实现接口),此解决方案将起作用。首先定义一个包含要比较的字段(getter)的接口:
interface ComparisionType {
String getFirstName();
String getLastName();
int getAge();
}
接下来,在您的学生
和员工
类中执行此操作,如下所示:
@Builder
@Getter
@Setter
@ToString
class Student implements ComparisionType {
String firstName;
String lastName;
int age;
float gpa;
}
@Builder
@Getter
@Setter
@ToString
class Employee implements ComparisionType {
String firstName;
String lastName;
int age;
float salary;
}
下面是示例可执行代码(java文档说明了每个方法的功能)
公共类StackOverflowTest{
公共静态void main(字符串[]args){
学生名单(
Student.builder().firstName(“firstName”).lastName(“lastName00”).age(35.build(),
Student.builder().firstName(“firstName1”).lastName(“lastname1”).age(22.build(),
Student.builder().firstName(“firstName2”).lastName(“lastName44”).age(30.build());
员工名单=员工名单(
Employee.builder().firstName(“firstName2”).lastName(“lastName11”).age(12.build(),
Employee.builder().firstName(“firstName3”).lastName(“lastName22”).age(23.build(),
Employee.builder().firstName(“firstName4”).lastName(“lastName33”).age(35.build());
List commonStudents=getCommon(学生、员工、Student.class、comparisontype::getAge);
System.out.println(普通学生);
List commonEmployees=getCommon(学生、员工、Employeer.class、比较类型::getLastName);
系统输出打印LN(普通员工);
}
/**
*在第一个和第二个参数中提供两个列表
*第三个参数是您喜欢哪种类型的公共对象作为输出
*第四个参数是要比较哪个字段以确定相似的对象
*/
@抑制警告(“未选中”)
私有静态列表getCommon(列出学生、员工、班级、,
功能(功能){
Map.Entry comparingand compariedto=getcomparingand compariedto(学生、员工、班级);
return(List)comparingand compariedto.getKey().stream()
.filter(isMatch(comparingand compariedto.getValue(),函数))
.collect(Collectors.toList());
}
/**
*标识要循环的列表
*与其他列表进行比较。
*/
私有静态映射。条目GetComparingandCompariedTo(列表1,
列表2,课堂讨论){
返回可选的.of(列表1)
.filter(list->list.get(0.getClass().equals(clazz))//Map.entry(list1,list2))
.orElse(地图条目(列表2、列表1));
}
/**
*获取一个列表并检查元素的字段
*匹配提供的字段(来自函数)。
*/
私有静态谓词isMatch(列表、函数){
返回类型->{
return list.stream()
.map(函数)
.anyMatch(field->field.equals(function.apply(type));
};
}
}
getCommon
方法将为您提供所需类型的对象列表,还将获取要比较的字段,因此您可以灵活选择
注意:我还没有测试这段代码的性能。如果您成功地计算了性能指标,请随意提及,我很想知道
PS:我正在使用Java11。这些列表有多大?我编辑了这个问题。列表可能包含多达10K个元素,并且应用程序对性能敏感。如果每个列表中没有重复项,我建议使用集合而不是列表。这将大大提高效率。您能否提供使用集合的示例代码?我不确定我是否理解。你只是为过滤器编写了另一个谓词。或者你是在问如何根据常用的命名属性自动创建一个谓词?因为这在Java中不是一件事,至少在不使用库的情况下不是。好的。这应该行得通。不过,我正在尝试看看是否有办法避免创建新类并阻止动态挖掘标准(例如第一次和第二次)
public class StackOverflowTest {
public static void main(String[] args) {
List<ComparisionType> students = List.of(
Student.builder().firstName("firstName").lastName("lastName00").age(35).build(),
Student.builder().firstName("firstName1").lastName("lastName11").age(22).build(),
Student.builder().firstName("firstName2").lastName("lastName44").age(30).build());
List<ComparisionType> employees = List.of(
Employee.builder().firstName("firstName2").lastName("lastName11").age(12).build(),
Employee.builder().firstName("firstName3").lastName("lastName22").age(23).build(),
Employee.builder().firstName("firstName4").lastName("lastName33").age(35).build());
List<Student> commonStudents = getCommon(students, employees, Student.class, ComparisionType::getAge);
System.out.println(commonStudents);
List<Employee> commonEmployees = getCommon(students, employees, Employee.class, ComparisionType::getLastName);
System.out.println(commonEmployees);
}
/**
* Supply the 2 lists in first and second argument
* Third argument is which type of common objects you like as the output
* Fourth argument is which field do you want to compare to determine similar objects
*/
@SuppressWarnings("unchecked")
private static <T> List<T> getCommon(List<ComparisionType> students, List<ComparisionType> employees, Class<T> clazz,
Function<ComparisionType, Object> function) {
Map.Entry<List<ComparisionType>, List<ComparisionType>> comparingAndComparedTo = getComparingAndComparedTo(students, employees, clazz);
return (List<T>) comparingAndComparedTo.getKey().stream()
.filter(isMatch(comparingAndComparedTo.getValue(), function))
.collect(Collectors.toList());
}
/**
* Identifies the list to loop through
* to compare against the other list.
*/
private static <T> Map.Entry<List<ComparisionType>, List<ComparisionType>> getComparingAndComparedTo(List<ComparisionType> list1,
List<ComparisionType> list2, Class<T> clazz) {
return Optional.of(list1)
.filter(list -> list.get(0).getClass().equals(clazz)) // <-- Add better class check here
.map(list -> Map.entry(list1, list2))
.orElse(Map.entry(list2, list1));
}
/**
* Takes a list and checks if a field of an element
* matches the supplied field (from the function).
*/
private static Predicate<ComparisionType> isMatch(List<ComparisionType> list, Function<ComparisionType, Object> function) {
return type -> {
return list.stream()
.map(function)
.anyMatch(field -> field.equals(function.apply(type)));
};
}
}