Получите наивысший балл от ArrayList с помощью Java Stream - PullRequest
0 голосов
/ 27 января 2019

Я хотел бы получить группу с наибольшим количеством баллов по Id. Если два наивысших балла совпадают, то я бы хотел получить наивысший балл на основе наименьшего необязательного идентификатора. Я хотел бы получить его в Java Stream. Итак, я пытаюсьследующие коды, которые не работают Пример:

Список массивов:

ID: 1 Счет: 80 OptionalId: 1
ID: 1 Счет: 90 OptionalId: 2
ID:1 Оценка: 90 OptionalId: 3
ID: 2 Оценка: 80 OptionalId: 1
ID: 2 Оценка: 100 OptionalId: 3
ID: 2 Оценка: 100 OptionalId: 5

Theрезультат должен быть

ID: 1 Score 90 OptionalId: 2
ID 2 Score 100 OptionalId: 3

Map<Long, Optional<Person>> result1 = records.stream()
                  .collect(Collectors.groupingBy(Person::getId,
                          Collectors.maxBy(Comparator.comparing(Person::getScore)),
                          Collector.minBy(Comparator.comparing(Person::getOptionalId))));


        for(Person ns: result1) {

            sb.append(ns.getBatchNumber());
            sb.append(',');

Ответы [ 4 ]

0 голосов
/ 27 января 2019

Для данного значения Id должен быть Person.Наличие значения Id зависит исключительно от личности.Таким образом, если существует идентификатор, тоже должен быть человек.Поэтому какой смысл иметь Optional<Person> в качестве значения карты.Напротив, имеет смысл просто иметь экземпляр Person в качестве значения в map.Здесь я использую коллектор toMap совместно с BinaryOperator.maxBy, чтобы выполнить работу.Вот как это выглядит.Обратите внимание на то, как BinaryOperator.maxBy используется как mergeFunction.

Map<Integer, Person> maxPersonById = records.stream()
    .collect(Collectors.toMap(Person::getId, Function.identity(),
        BinaryOperator.maxBy(Comparator.comparing(Person::getScore)
            .thenComparing(Comparator.comparing(Person::getOptionalId).reversed()))));

. И вот вывод для указанного выше ввода.

{1=Person [id=1, score=90, optionalId=2], 2=Person [id=2, score=100, optionalId=3]}
0 голосов
/ 27 января 2019

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

import static java.util.Collections.reverseOrder;
import static java.util.Comparator.comparing;

Map<Long, Optional<Person>> result1 = records.stream()
    .collect(Collectors.groupingBy(Person::getId,
             Collectors.maxBy(
                 Comparator.comparing(Person::getScore)
                  .thenComparing(reverseOrder(comparing(Person::getOptionalId))))));

Optional[ID: 1 Score: 90 OptionalId: 2]
Optional[ID: 2 Score: 100 OptionalId: 3]

Хитрость здесь в том, чтобы изменить порядок сортировки только для необязательного идентификатора, который мы хотим, чтобы он был восходящим, а не нисходящим.Порядок сортировки по умолчанию будет убывающим, потому что мы вызываем Collections.maxBy.

Я цитирую этот замечательный вопрос SO для помощи с обратным синтаксисом.Кроме того, я позаимствовал код плиты котла у @mayamar для настройки следующей демонстрации:

Демо

(демонстрация только для демонстрационных целей)

0 голосов
/ 27 января 2019

Я предлагаю вам начать с пользовательского Comparator<Person>, в котором приоритет имеет максимум score, а затем минимум optionalId.Это хороший способ передать его в переменную для краткости:

final Comparator<Person> comparator = Comparator
    .comparing(Person::getScore)                 // descending score first
    .thenComparing(Comparator                    // then ..
        .comparing(Person::getOptionalId)        // .. optionalId
        .reversed());                            // .. but ascending

Теперь используйте некоторые сборщики, использующие .

  • Collectors::groupingBy для группировки всех Person s по id и последующего значения
  • Collectors::reducing для уменьшения всех Person с одинаковымиid в один, используя Comparator<Person>, чтобы получить тот с самым высоким score и самым низким optionalId.
  • Collectors::collectingAndThen, чтобы выровнять структуру от Collection<Optional<Person>> доCollection<Person>, поскольку любая операция восстановления приводит к Optional - этот шаг не является обязательным и может быть пропущен в соответствии с вашим образцом.

Вот код:

Collection<Person> filtered = records.stream()         // Stream<Person>
    .collect(Collectors.groupingBy(                    // from Map<Long, List<Person>>
        Person::getId,
        Collectors.collectingAndThen(                  // .. downstream to ..
                Collectors.reducing((a, b) ->          // .. Map<Long, Optional<Person>>
                    comparator.compare(a, b) > 0 ? a : b),
                Optional::get))                        // .. Map<Long, Person>
    .values();                                         // .. Collection<Person>

[Персона [id = 1, оценка = 90, необязательный ID = 2], Персона [id = 2, оценка = 100, необязательный ID = 3]]

0 голосов
/ 27 января 2019

Я немного изменил и ввел вспомогательный класс, который сравнивает оценку и необязательный идентификатор.

public class T21Group {

public static void main(String[] args) {
    List<Person> records = new ArrayList<>();
    records.add(new Person(1, 80, 1));
    records.add(new Person(1, 90, 2));
    records.add(new Person(1, 90, 3));
    records.add(new Person(2, 80, 1));
    records.add(new Person(2, 100, 3));
    records.add(new Person(2, 100, 5));

    Map<Long, Optional<Person>> result1 = records.stream()
            .collect(Collectors.groupingBy(Person::getId, Collectors.maxBy(Comparator.comparing(Pair::new))));

    for (Optional<Person> ns : result1.values()) {
        System.out.println(ns);
    }
}

public static class Pair implements Comparable<Pair> {
    long score;
    long optionalId;

    public Pair(Person p) {
        score = p.getScore();
        optionalId = p.getOptionalId();
    }

    @Override
    public int compareTo(Pair o) {
        if (this.score == o.score) {
            return Long.compare(o.optionalId, this.optionalId);
        }
        return Long.compare(this.score, o.score);
    }

}

public static class Person {
    private long id;
    private long score;
    private long optionalId;

    public Person(long id, long score, long optionalId) {
        this.id = id;
        this.score = score;
        this.optionalId = optionalId;
    }

    @Override
    public String toString() {
        return "ID: " + id + " Score: " + score + " OptionalId: " + optionalId;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public long getScore() {
        return score;
    }

    public void setScore(long score) {
        this.score = score;
    }

    public long getOptionalId() {
        return optionalId;
    }

    public void setOptionalId(long optionalId) {
        this.optionalId = optionalId;
    }

}

}
...