If you don't want to add any new classes, and if you have the flexibility to modify them (implement an interface), this solution will work. First define an interface that contains the fields (getters) you want to compare:
interface ComparisionType {
String getFirstName();
String getLastName();
int getAge();
}
Next, implement this in your Student and Employee class like so:
@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;
}
Below is the sample executable code (with java-doc telling what each method does).
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)));
};
}
}
The getCommon method will give you the list of objects of your desired type and also takes in the field you want to compare, so you have the flexibility to choose.
NOTE: I haven't tested the performance for this code. If you managed to calculate the performance metrics, feel free to mention it, i'm curious to know.
PS: I'm using Java11.