java中两个集合之间的公共元素

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 } 我需要找出与学生同名的员

我试图在2个元素为Object类型的列表之间找到公共元素

比如说

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