java группировать потоки и находить 10 лучших за одну итерацию - PullRequest
1 голос
/ 14 июля 2020

Отложено от этого Как применить сортировку и ограничение после группы, используя Java потоков , потому что я хочу решить эту проблему ровно за одну итерацию

Представьте себе У меня есть следующая сущность:

public static class Hospital {
    private AREA area;
    private int patients;

    public Hospital(AREA area, int patients) {
        this.area = area;
        this.patients = patients;
    }

    public AREA getArea() {
        return area;
    }

    public void setArea(AREA area) {
        this.area = area;
    }

    public int getPatients() {
        return patients;
    }

    public void setPatients(int patients) {
        this.patients = patients;
    }
}

public enum AREA {
    AREA1,
    AREA2,
    AREA3
}

Теперь, учитывая список больниц, я хочу найти районы с большинством пациентов в них, вот что я сделал до сих пор:

public static void main(String[] args) {
    List<Hospital> list = Arrays.asList(
            new Hospital(AREA.AREA1, 20),
            new Hospital(AREA.AREA2, 10),
            new Hospital(AREA.AREA1, 10),
            new Hospital(AREA.AREA3, 40),
            new Hospital(AREA.AREA2, 10));
    Map<AREA, Integer> map = findTopTen(list);
    for (AREA area : map.keySet())
        System.out.println(area);

}

public static Map<AREA, Integer> findTopTen(Iterable<Hospital> iterable) {
    Map<AREA, Integer> map = StreamSupport.stream(iterable.spliterator(), false)
            .collect(Collectors.groupingBy(Hospital::getArea,
                    Collectors.summingInt(Hospital::getPatients)));
    for (Map.Entry<AREA, Integer> area : map.entrySet())
        System.out.println(area.getKey() + "...." + area.getValue());
    return map.entrySet().stream()
            .sorted((e1, e2) -> e2.getValue() - e1.getValue())
            .collect(Collectors.toMap(Map.Entry::getKey,
                    Map.Entry::getValue, (o, o2) -> o,
                    LinkedHashMap::new));

}

Ясно, что я Итерировал дважды, чтобы найти десять областей с наибольшим количеством пациентов (один раз для группировки больниц по областям и расчета суммирования для этой группы и еще раз для поиска десяти областей с наибольшим числом пациентов).

Сейчас Я хочу знать:

1) Есть ли лучший подход для решения этой проблемы в одном потоке и, следовательно, в одной итерации?

2) Есть ли какое-либо преимущество в производительности для выполнения этого за одну итерацию, как лучше всего решить эту проблему? (С моей точки зрения, с одной стороны, когда я вызываю collect, что является операцией терминала в первый раз использует мою итерацию и сохраняет промежуточный результат в другом объекте, в моем коде я назвал этот объект iterationOneResult, поэтому использование одного потока и вызов метода collect один раз пропустит этот промежуточный результат, что является основным преимуществом использования потока в java на другом ручное решение этой задачи за одну итерацию снижает сложность с O (2n) до O (n))

1 Ответ

2 голосов
/ 14 июля 2020

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

Map<AREA, Integer> map = list.stream()
        .collect(Collectors.groupingBy(Hospital::getArea, Collectors.summingInt(Hospital::getPatients)))
        .entrySet().stream()
        .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
        .limit(10)
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

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

.peek(e -> System.out.println(e.getKey() + " " + e.getValue()))

сразу после .entrySet().stream()

...