我正在尝试使用Java8中引入的Comparator.comparing()构建自定义比较逻辑。
的要求是:如果在Person类中引入了一个新字段,那么代码应该可以工作。它应该把新的领域纳入比较逻辑。
这意味着比较逻辑应该是这样的: id -> name --> newfield1 --> newfield2
见下面的伪代码:
package test;
import java.lang.reflect.Field;
import java.util.Comparator;
public class Test6 {
public static void main (String args []) {
Field[] declaredFields = Person.class.getDeclaredFields();
Comparator<Person> comparator = Comparator.comparing((Person x)-> x.getId());
for (int i=1; i<declaredFields.length ; i++) {
comparator = comparator.thenComparing(field[i]); // what code should I place over here, to make it work for field obtained above.
}
List<Person> persons = new ArrayList<>();
persons.stream().sorted(comparator).collect(Collectors.toList());
}
}
class Person{
int id ;
String name;
// if we add new field , then this field should also be included in comparison logic
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}发布于 2021-02-10 08:36:54
在中,有一个基于反射的比较器BeanUtils。结合ReverseComparator,可以编写动态比较器。
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>简单的例子:
public static <T> Comparator<T> beanComparator(String fieldName, boolean asc) {
Comparator<T> propertyComparator;
if (asc) {
propertyComparator = new BeanComparator<>(fieldName);
} else {
propertyComparator = new ReverseComparator(new BeanComparator<>(fieldName));
}
return propertyComparator;
}发布于 2021-02-08 18:13:55
要做您想要做的事情是有问题的,因为您需要使用反射将一个函数(它描述一个返回类型)传递给比较器。因为我认为您无论如何都需要修改类,以包含新字段以及它们的getter和构造函数。通过这样做,您可以指定在比较中应用getter的顺序。
class Person implements Comparable<Person> {
int id;
String name;
String foo;
private Comparator<Person> comparing = Comparator.comparing(Person::getId);
{
comparing = comparing.thenComparing(Person::getName);
comparing = comparing.thenComparing(Person::getFoo);
// as new fields/getters are added, also append (or insert) the
// comparator.
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int compareTo(Person p) {
return comparing.compare(this, p);
}
public String getFoo() {
return foo;
}
}然后你就可以这样称呼它。
List<Person> sortedPersons = persons.stream().sorted()
.collect(Collectors.toList());或者如果他们已经在List中
persons.sort(null); // null == natural ordering.发布于 2021-02-10 23:15:18
您可以使用Field.get方法,它返回作为参数传递的对象上该字段的值。get抛出一个已检查的异常,因此我们必须捕获它并作为一个未检查的异常重新抛出。
Field[] fields = Person.class.getDeclaredFields();
Comparator<Person> comparator = Comparator.comparing(Person::getId);
for (int i = 1; i < fields.length; i++) {
// variable referenced in lambda must be final or effectively final
final Field f = fields[i];
comparator = comparator.thenComparing((Person person) -> {
try {
return (Comparable) f.get(person);
} catch(IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}这将导致关于未检查的方法调用和未检查的转换的警告,这可以通过方法上的@SuppressWarnings("unchecked")注释来抑制。警告是真实的:如果Person有一个类型不是Comparable的字段,那么这将在运行时失败。您可能希望先检查f的类型是否实现了Comparable,并且只更新比较器;我没有为您这样做。
还请注意,如果id不再是数组中的第一个字段,则从索引1开始循环的代码很可能会中断;我建议简单地循环整个数组,这将多余地第二次包含id字段,但这不会造成任何损害,只会带来较小的性能成本。
https://stackoverflow.com/questions/66103523
复制相似问题