英文:
Streams Sorting by runtime parameter
问题
我有一个自定义的比较器 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;
}
// 获取器和设置器
}
(以上内容为您要翻译的部分。)
英文:
I have custom Comparator ChainedComparator and and it has multiple comparator in it, which will be passed at runtime.
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);
}
}
now i want to create comparator at run time from the field name age , rather than hardcoding like below
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))
any advice please
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
}
答案1
得分: 4
最简单静态的方式可能是维护一个字段名到比较器映射,就像这样:
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 自己的 thenComparing 来组合比较器(而不是自己实现它)。
这里是一个使用反射动态选择要排序字段的示例实现。
在使用之前,请注意:
- 它假设字段是
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 -> 你的类名
.reduce(Comparator::thenComparing);
personList.stream().sorted(chainedComparator.orElseThrow())...;
你可以通过使用泛型来扩展,以使其不仅限于 Person 类。
英文:
The simplest, static way to do this may be to just maintain a field name -> Comparator map, like this:
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)
);
Which, given a field list like
List<String> sortFields = Arrays.asList("age", "firstName", "lastName");
can be used to produce a composed comparator:
Optional<Comparator<Person>> chainedComparator =
sortFields.stream().map(comparatorMap::get).reduce(Comparator::thenComparing);
personList.stream().sorted(chainedComparator.get());
You can also notice that I used Comparator's own thenComparing to compose comparators (instead of implementing it yourself).
Here's an example implementation using reflection to dynamically select the fields to sort by
Before it's used, note:
-
It assumes that fields are of
Comparabletypes (if you pass the name of a field whose type is not comparable, a class cast exception will be raised) -
I'm creating a comparator each time, you may want to store them against the field name, if that's necessary
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 }}
And with that, you can change the initial implementation to something like:
Optional<Comparator<Person>> chainedComparator = sortFields.stream()
.map(Main::personFieldComparator) //Main -> Your class name
.reduce(Comparator::thenComparing);
personList.stream().sorted(chainedComparator.orElseThrow())...;
You may choose to extend this by using generics to make it not Person-specific
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论