Comparator.comparing(...).thenComparing(...) 找出哪些字段不匹配
Comparator.comparing(...).thenComparing(...) find out which fields did not match
我正在尝试比较相同 class 的两个对象,目标是比较它们并确定哪些字段不匹配。
我的域名示例 class
@Builder(toBuilder=true)
class Employee {
String name;
int age;
boolean fullTimeEmployee;
}
两个对象
Employee emp1 = Employee.builder().name("john").age(25).fullTime(false).build();
Employee emp2 = Employee.builder().name("Doe").age(25).fullTime(true).build();
比较两个对象
int result = Comparator.comparing(Employee::getName, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Employee::getAge, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Employee::isFullTimeEmployee, Comparator.nullsFirst(Comparator.naturalOrder()))
.compare(emp1, emp2);
结果将是 0
,因为 name
和 fullTime
字段彼此不匹配。
但我还想生成一个不匹配的字段列表.. 如下所示
List<String> unmatchedFields = ["name","fulltimeEmployee"];
除了一堆 if() else
之外,我可以用更好的方式来做吗?
首先,我认为您不了解 comparing().thenComparing() 的工作原理。你的情况的结果不会是 0。比较器比较第一个语句,如果它们相等,它会转到 thenComparing() 等等。当且仅当所有 comparison()/thenComparing() 也相等时,结果将为 0。我几天前写过这篇文章:http://petrepopescu.tech/2021/01/simple-collection-manipulation-in-java-using-lambdas/
无论如何,回到您的问题,我认为您正在寻找一个 equal() ,其中它还 returns 哪些字段不相等。这是一个快速原型。
public class Pair<T, U> {
private Function<? super T, ? extends Comparable> function;
private String fieldName;
public Pair(Function<? super T, ? extends Comparable> function, String fieldName) {
this.function = function;
this.fieldName = fieldName;
}
}
实际比较器:
public class MyComparator {
List<Pair> whatToCompare = Arrays.asList(
new Pair<Employee, String>(Employee::getName, "name"),
new Pair<Employee, String>(Employee::getAge, "age"),
new Pair<Employee, Double>(Employee::isFullTimeEmployee, "fullTimeEmployee")
);
public List<String> compare(Employee e1, Employee e2) {
List<String> mismatch = new ArrayList<>();
for(Pair pair:whatToCompare) {
int result = Comparator.comparing(pair.getFunction()).compare(e1, e2);
if (result != 0) {
mismatch.add(pair.getFieldName());
}
}
return mismatch;
}
}
查看 DiffBuilder。它可以报告哪些项目不同。
DiffResult<Employee> diff = new DiffBuilder(emp1, emp2, ToStringStyle.SHORT_PREFIX_STYLE)
.append("name", emp1.getName(), emp2.getName())
.append("age", emp1.getAge(), emp2.getAge())
.append("fulltime", emp1.getFulltime(), emp2.getFulltime())
.build();
DiffResult
有一个 getDiffs()
方法,您可以循环查找不同之处。
Comparator
本身没有发现差异的能力,它只比较两个相同类型的对象和returns -1
、0
或1
.这是接口契约。
要发现实际差异,需要使用反射API获取字段,比较两个传递对象的真实值是否相等,并列出差异。这是一个通用的实现:
public class Difference<T> {
T left;
T right;
//all-args constructor omitted
public Set<String> differences() throws IllegalAccessException {
// fieldName-field as a key-value for the left and right instances
Map<String, Field> leftFields = Arrays.stream(left.getClass().getDeclaredFields())
.peek(f -> f.setAccessible(true))
.collect(Collectors.toMap(Field::getName, Function.identity()));
Map<String, Field> rightFields = Arrays.stream(left.getClass().getDeclaredFields())
.peek(f -> f.setAccessible(true))
.collect(Collectors.toMap(Field::getName, Function.identity()));
// initialize Set as long as two fields cannot have a same name
Set<String> differences = new HashSet<>();
// list the fields of the left instance
for (Entry<String, Field> entry: leftFields.entrySet()) {
String fieldName = entry.getKey();
Field leftField = entry.getValue();
// find the right instance field matching the name
Field rightField = rightFields.get(fieldName);
// compare their actual values and add among the differences if they aren't equal
if (!leftField.get(left).equals(rightField.get(right))) {
differences.add(fieldName);
}
}
return differences;
}
}
Employee emp1 = Employee.builder().name("john").age(25).fullTime(false).build();
Employee emp2 = Employee.builder().name("Doe").age(25).fullTime(true).build();
Set<String> differences = new Difference<Employee>(emp1, emp2).differences();
System.out.println(differences);
[name, fullTimeEmployee]
我正在尝试比较相同 class 的两个对象,目标是比较它们并确定哪些字段不匹配。
我的域名示例 class
@Builder(toBuilder=true)
class Employee {
String name;
int age;
boolean fullTimeEmployee;
}
两个对象
Employee emp1 = Employee.builder().name("john").age(25).fullTime(false).build();
Employee emp2 = Employee.builder().name("Doe").age(25).fullTime(true).build();
比较两个对象
int result = Comparator.comparing(Employee::getName, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Employee::getAge, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Employee::isFullTimeEmployee, Comparator.nullsFirst(Comparator.naturalOrder()))
.compare(emp1, emp2);
结果将是 0
,因为 name
和 fullTime
字段彼此不匹配。
但我还想生成一个不匹配的字段列表.. 如下所示
List<String> unmatchedFields = ["name","fulltimeEmployee"];
除了一堆 if() else
首先,我认为您不了解 comparing().thenComparing() 的工作原理。你的情况的结果不会是 0。比较器比较第一个语句,如果它们相等,它会转到 thenComparing() 等等。当且仅当所有 comparison()/thenComparing() 也相等时,结果将为 0。我几天前写过这篇文章:http://petrepopescu.tech/2021/01/simple-collection-manipulation-in-java-using-lambdas/
无论如何,回到您的问题,我认为您正在寻找一个 equal() ,其中它还 returns 哪些字段不相等。这是一个快速原型。
public class Pair<T, U> {
private Function<? super T, ? extends Comparable> function;
private String fieldName;
public Pair(Function<? super T, ? extends Comparable> function, String fieldName) {
this.function = function;
this.fieldName = fieldName;
}
}
实际比较器:
public class MyComparator {
List<Pair> whatToCompare = Arrays.asList(
new Pair<Employee, String>(Employee::getName, "name"),
new Pair<Employee, String>(Employee::getAge, "age"),
new Pair<Employee, Double>(Employee::isFullTimeEmployee, "fullTimeEmployee")
);
public List<String> compare(Employee e1, Employee e2) {
List<String> mismatch = new ArrayList<>();
for(Pair pair:whatToCompare) {
int result = Comparator.comparing(pair.getFunction()).compare(e1, e2);
if (result != 0) {
mismatch.add(pair.getFieldName());
}
}
return mismatch;
}
}
查看 DiffBuilder。它可以报告哪些项目不同。
DiffResult<Employee> diff = new DiffBuilder(emp1, emp2, ToStringStyle.SHORT_PREFIX_STYLE)
.append("name", emp1.getName(), emp2.getName())
.append("age", emp1.getAge(), emp2.getAge())
.append("fulltime", emp1.getFulltime(), emp2.getFulltime())
.build();
DiffResult
有一个 getDiffs()
方法,您可以循环查找不同之处。
Comparator
本身没有发现差异的能力,它只比较两个相同类型的对象和returns -1
、0
或1
.这是接口契约。
要发现实际差异,需要使用反射API获取字段,比较两个传递对象的真实值是否相等,并列出差异。这是一个通用的实现:
public class Difference<T> {
T left;
T right;
//all-args constructor omitted
public Set<String> differences() throws IllegalAccessException {
// fieldName-field as a key-value for the left and right instances
Map<String, Field> leftFields = Arrays.stream(left.getClass().getDeclaredFields())
.peek(f -> f.setAccessible(true))
.collect(Collectors.toMap(Field::getName, Function.identity()));
Map<String, Field> rightFields = Arrays.stream(left.getClass().getDeclaredFields())
.peek(f -> f.setAccessible(true))
.collect(Collectors.toMap(Field::getName, Function.identity()));
// initialize Set as long as two fields cannot have a same name
Set<String> differences = new HashSet<>();
// list the fields of the left instance
for (Entry<String, Field> entry: leftFields.entrySet()) {
String fieldName = entry.getKey();
Field leftField = entry.getValue();
// find the right instance field matching the name
Field rightField = rightFields.get(fieldName);
// compare their actual values and add among the differences if they aren't equal
if (!leftField.get(left).equals(rightField.get(right))) {
differences.add(fieldName);
}
}
return differences;
}
}
Employee emp1 = Employee.builder().name("john").age(25).fullTime(false).build();
Employee emp2 = Employee.builder().name("Doe").age(25).fullTime(true).build();
Set<String> differences = new Difference<Employee>(emp1, emp2).differences();
System.out.println(differences);
[name, fullTimeEmployee]