java - 流按运行时参数排序
问题描述
我有自定义比较ChainedComparator
器,它有多个比较器,将在运行时传递。
class ChainedComparator implements Comparator<Person> {
private List<Comparator<Person>> comparatorList;
public int compare(Person o1, Person o2) {
for (Comparator<Person> comparator : comparatorList) {
int result = comparator.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
@SafeVarargs
public ChainedComparator(Comparator<Person>... comparators) {
this.comparatorList = Arrays.asList(comparators);
}
}
现在我想在运行时从字段名称age创建比较器,而不是像下面这样的硬编码
Comparator<Person> ageComparator = Comparator.comparing(Person::getAge);
Comparator<Person> lastNameComparator = Comparator.comparing(Person::getLastName);
Comparator<Person> ageComparator = Comparator.comparing(Person::getAge);
personList.stream().sorted(new ChainedComparator(firstNameComparator,ageComparator))
请有任何建议
class Person {
Person(){}
String firstName;
String lastName;
int age;
String country;
public Person( String firstName, String lastName, int age,String country) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.country = country;
}
// getters and setters
}
解决方案
最简单的静态方法可能是只维护一个字段名称 -> 比较器映射,如下所示:
private static Map<String, Comparator<Person>> comparatorMap = Map.of(
"age", Comparator.comparing(Person::getAge),
"firstName", Comparator.comparing(Person::getFirstName),
"lastName", Comparator.comparing(Person::getLastName),
"country", Comparator.comparing(Person::getCountry)
);
其中,给定一个字段列表,例如
List<String> sortFields = Arrays.asList("age", "firstName", "lastName");
可用于生成组合比较器:
Optional<Comparator<Person>> chainedComparator =
sortFields.stream().map(comparatorMap::get).reduce(Comparator::thenComparing);
personList.stream().sorted(chainedComparator.get());
您还可以注意到,我使用Comparator
's ownthenComparing
来组成比较器(而不是自己实现它)。
这是一个使用反射来动态选择要排序的字段的示例实现
在使用它之前,请注意: * 它假定字段是Comparable
类型的(如果您传递类型不可比较的字段的名称,则会引发类转换异常) * 我每次都在创建一个比较器,您可能想要如果有必要,将它们存储在字段名称中
private static Comparator<Person> personFieldComparator(String field) {
return (person1, person2) -> readPersonField(person1, field)
.compareTo(readPersonField(person2, field));
}
private static Comparable<Object> readPersonField(Person person, String field) {
try {
Field f = Person.class.getDeclaredField(field);
f.setAccessible(true);
return (Comparable<Object>) f.get(person);
} catch (IllegalAccessException | IllegalArgumentException
| NoSuchFieldException e) {
throw new RuntimeException(e); //proper handling needed
}
}
有了这个,您可以将初始实现更改为:
Optional<Comparator<Person>> chainedComparator = sortFields.stream()
.map(Main::personFieldComparator) //Main -> Your class name
.reduce(Comparator::thenComparing);
personList.stream().sorted(chainedComparator.orElseThrow())...;
您可以选择通过使用泛型来扩展它以使其不Person
特定
推荐阅读
- python - 使用 Python 解析 XML 文档 - 搜索特定元素,获取内部字符串
- android - 为什么我会收到 SYSTEM_ALERT_WINDOW 权限要求警告?
- http-headers - 如何设置内容安全策略标头?
- java - 带有 2 个按钮的简单 jframe,1 个按钮启动 ServerSocket,其他按钮停止它
- javascript - React 16.13:useState 在渲染前等待状态更新
- exception - 在 Quarkus 中使用 RedirectionException 时出现有线异常
- database - 如何在 Cassandra 中按上次更新日期对数据进行排序?
- postgresql - 如何根据同一个表在另一个数据库中更新一个数据库中的表记录?
- python - 将元素添加到字典中的嵌套数组,将序列转换为具有嵌套数组的可读字典python
- reactjs - 在 CRA 应用程序中使用 SVG 文件作为 React 组件