英文:
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
Comparable
types (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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论