Как получить объект, который имеет максимальное значение для нескольких атрибутов, используя поток? - PullRequest
0 голосов
/ 08 февраля 2019

Скажи, у меня есть группа злодеев.Они характеризуются тем, насколько они хороши, плохи или уродливы.

static class Villain {
    String name;
    int good;
    int bad;
    int ugly;

    Villain(String name, int good, int bad, int ugly) {
        this.name = name;
        this.good = good;
        this.bad = bad;
        this.ugly = ugly;
    }
}

Хорошо, познакомьтесь с бандой:

List<Villain> villains = new ArrayList<>();
villains.add(new Villain("Bob", 2, 2, 1));
villains.add(new Villain("Charley", 2, 1, 2));
villains.add(new Villain("Dave", 2, 1, 1));
villains.add(new Villain("Andy", 2, 2, 2));
villains.add(new Villain("Eddy", 1, 2, 2));
villains.add(new Villain("Franz", 1, 2, 1));
villains.add(new Villain("Guy", 1, 1, 2));
villains.add(new Villain("Harry", 1, 1, 1));

Я хочу выяснить, ктосамый лучший, худший и самый уродливый.С этим я хочу выяснить, кто является лучшим.В случае галстука, кто хуже?В случае ничьей, кто самый уродливый.

Мне удалось это сделать с помощью приведенного ниже кода.

List<Villain> bestVillains = villains
        .stream()
        .collect(groupingBy(v -> v.good, TreeMap::new, toList()))
        .lastEntry()
        .getValue()
        .stream()
        .collect(groupingBy(v -> v.bad, TreeMap::new, toList()))
        .lastEntry()
        .getValue()
        .stream()
        .collect(groupingBy(v -> v.ugly, TreeMap::new, toList()))
        .lastEntry()
        .getValue();

Это действительно приводит к List<Villain> только с одним членом: Энди.Он действительно лучший, худший и уродливый!

Однако у меня довольно много повторений кода, сбор значений, повторное их превращение в потоки и т. Д. Какие-нибудь предложения о том, как это очистить?

Как это обрабатывается JVM.Последовательно или какая-то магия происходит под капотом?

Ответы [ 4 ]

0 голосов
/ 08 февраля 2019

Если вас просто беспокоит дублирование кода , попробуйте извлечь код и использовать его повторно, например:

final BiFunction<List<Villain>, Function<Villain, Object>, List<Villain>> func = (listVillians, villianAttribute) -> listVillians
    .stream()
    .collect(groupingBy(villianAttribute, TreeMap::new, toList()))
    .lastEntry()
    .getValue();

И использовать его следующим образом:

List<Villain> bestVillainsMK2 = func.apply(func.apply(func.apply(villains, Villain::getGood), Villain::getBad), Villain::getUgly);

Примечание: я добавил мнимые геттеры в класс Villain.

Внутренний вызов большинства использует исходный список, остальные используют возврат этих функций.

Здесь как выделенная функция

private static List<Villain> func2(List<Villain> listVillians, Function<Villain, Object> villianAttribute) {
    return listVillians.stream()
      .collect(groupingBy(villianAttribute, TreeMap::new, toList()))
      .lastEntry()
      .getValue();
}

Использование практически идентично

List<Villain> bestVillainsMK3 = func2(func2(func2(villains, Villain::getGood), Villain::getBad), Villain::getUgly);

Но если вас также интересует правильный инструмент или шаблон для вашей ситуации см. @ nullpointer s подход с использованием компараторов.

0 голосов
/ 08 февраля 2019

Таким образом, идея состоит в том, что сначала рассматривается, какое значение имеет наибольшее значение для добра, затем (в случае связи), которое имеет наибольшее значение для плохого, затем (если оно все еще не является решающим), которое имеетсамое высокое значение для 'уродливый'

Вы скорее хотите сортировать, используя следующие значения Comparator для Villian s:

Comparator<Villain> villainComparator = Comparator.comparingInt(Villain::getGood)
    .thenComparingInt(Villain::getBad)
    .thenComparingInt(Villain::getUgly);

Villain result = villains.stream()
                         .max(villainComparator)
                         .orElse(null);
0 голосов
/ 08 февраля 2019

Вам нужен компаратор.Вы можете добавить это в свой поток.Это будет выглядеть так:

List<Villain> bestVillains = villains.stream()
        .sorted((o1, o2) -> {
            if(o2.good == o1.good){
                if(o2.bad == o1.bad){
                    return o2.ugly - o1.ugly;
                }else{
                    return o2.bad - o1.bad;
                }
            }else{
                return o2.good - o1.good;
            }
        })
        .limit(1)
        .collect(Collectors.toList());

Это выдаст список из 1 злодея - худшего из всех.То, что происходит здесь, это то, что компаратор сортирует только в обратном порядке, а затем вы берете первую запись.

0 голосов
/ 08 февраля 2019

Вы можете использовать вложенную группировкуBy

TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, List<Villain>>>> collect = 
    villains.stream()
        .collect(groupingBy(v -> v.good, TreeMap::new,
                     groupingBy(v -> v.bad, TreeMap::new,
                         groupingBy(v -> v.ugly, TreeMap::new, mapping(o -> o, toList())))));

Затем распечатать ее:

System.out.println(collect.lastEntry().getValue()
                      .lastEntry().getValue()
                          .lastEntry().getValue());
...