Сортировка потоков по параметру времени выполнения - PullRequest
3 голосов
/ 07 апреля 2020

У меня есть собственный компаратор 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);
    }
}

Теперь я хочу создать компаратор во время выполнения из имени поля возраст , а не жесткий код, как показано ниже

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
}

1 Ответ

3 голосов
/ 07 апреля 2020

Самый простой, статичный c способ сделать это может состоять в том, чтобы просто сохранить имя поля -> Карта компаратора, например:

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 для составления компараторов (вместо того, чтобы реализовывать его самостоятельно).


Вот пример реализации , использующий отражение для динамического выбора полей для сортировки по

Перед его использованием, обратите внимание: * Предполагается, что fields относятся к 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 -specifi c

...