Как можно улучшить эту итерацию списка с помощью потока? - PullRequest
5 голосов
/ 10 марта 2019

Я пытаюсь подсчитать, сколько раз Int встречается в поле в списке объектов.

Это код, который у меня есть

TreeMap<Integer, Double> ratings = new TreeMap();
ArrayList<Establishment> establishments = new ArrayList<>();

double one = 0;
double two = 0;
double three = 0;
double five = 0;

for (Establishment e : establishments) {
    if (e.getRating() == 1) {
        one++;
    }
    if (e.getRating() == 2) {
        two++;
    }
    if (e.getRating() == 3) {
        three++;
    }
    if (e.getRating() == 5) {
        five++;
    }
}

    ratings.put(1, (one / establishments.size()) * 100);
    ratings.put(2, (two / establishments.size()) * 100);
    ratings.put(3, (three / establishments.size()) * 100);
    ratings.put(5, (five / establishments.size()) * 100);

все же это не идеально, если добавить больше оценок (скажем, 20+), то у вас будет куча двойников, и это не будет обслуживаться.

Я знаю, что мог бы что-то сделать с Stream, если бы у меня был список целых, скажем,

listOfInts.stream().filter(i -> i == 3).count()

пока это список объектов, которые содержат int, и мне нужно вычислить количество оценок == X в этом списке объектов.

так псевдокод для чего мне нужно:

Establishemnts.getAllRatings (). Stream (). Filter (рейтинги -> рейтинги == 3) .count () *

* Повторение для каждого из типов рейтинга 1 - 5

** Нет getAllRatings - думаю, это проблема, которую я пытаюсь решить)

Ответы [ 5 ]

5 голосов
/ 10 марта 2019

Использование более или менее только потоков:

List<Establishment> establishments = new ArrayList<>();
Map<Integer, Double> ratings = establishments.stream()
        .collect(Collectors.groupingBy(Establishment::getRating, Collectors.counting()))
        .entrySet()
        .stream()
        .collect(Collectors.toMap(e -> e.getKey(), 
                                  e -> 100.0 * (e.getValue() / establishments.size())));
5 голосов
/ 10 марта 2019

Вы можете сделать:

Map<Integer, Double> ratings = new TreeMap<>();
List<Establishment> establishments = new ArrayList<>();
establishments.stream() 
              .collect(Collectors.groupingBy(Establishment::getRating, Collectors.counting()))
              .forEach((k, v) -> ratings.put(k, (double)v/establishments.size() * 100));

Который будет использовать Collectors::groupingBy с Collectors::counting, который создаст Map, состоящий из подсчета рейтингов, затем используйте forEach, чтобы добавить их TreeMap

Или, как предлагает VGR, еще более элегантное использование Collectors::collectingAndThen:

Map<Integer, Double> ratings =
establishments.stream()
              .collect(
                       Collectors.groupingBy(Establishment::getRating, 
                       Collectors.collectingAndThen(Collectors.counting(), c -> c * 100.0 / establishments.size())
               ));

, который будет непосредственно создавать Map без необходимости создавать Map, перетекать поверх него снова, а затем снова собирать в Map

3 голосов
/ 10 марта 2019

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

List<Integer> attributes = establishments.stream().map(es -> es.getRating()).collect(Collectors.toList());

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

establishments.stream().map(es -> es.getRating()).collect(Collectors.toList()).stream().filter(ratings -> ratings == 3).count()

Если вы хотите получить количество оценок / процент для всех оценок, пожалуйста, используйте код в ответе @ Marek

0 голосов
/ 10 марта 2019

Чтобы получить TreeMap<Integer, Double> ratings, вы можете использовать потоковый API с groupingBy и summingDouble в качестве нисходящего потока:

TreeMap<Integer, Double> ratings = establishments.stream()
        .collect(Collectors.groupingBy(Establishment::getRating, TreeMap::new,
                Collectors.summingDouble(v -> ((1.0 / establishments.size())* 100))));

И для подсчетаколичество раз:

long count = establishments.stream()
        .filter(e -> e.getRating() == 3)
        .count();
0 голосов
/ 10 марта 2019

В цикле вы можете суммировать значения:

for (Establishment e : establishments) {
    // Get old value OR set to "0"
    Double rating = ratings.getOrDefault(e.getRating(), 0);
    // increase rating (counter)
    ratings.put(e.getRating(), rating + 1);
}

И вычислите среднее значение в конце:

// Calculate part of equation which is not changing in loop
int size = establishments.size() * 100;

for (Map.Entry<Integer, Double> entry : ratings.entrySet()) {
    // Calculate average
    Double average = entry.getValue() / size;
    // Add it to the map
    ratings.put(entry.getKey(), average);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...