Использование Java Streams для группировки списка объектов по атрибуту и ​​сокращения их до нового списка объектов со средним значением другого атрибута - PullRequest
0 голосов
/ 10 декабря 2018

У меня есть список сенсорных POJO

public class SensorSample {

    private Device.SensorType sensorType; // This is an enum
    private double sample;
    private long timestamp;

    // Constructor

    // Setters

    // Getters

}

Мне нужно сгруппировать их по timestamp, чтобы все SensorSample одного дня были вместе.Затем мне нужно уменьшить их так, чтобы у меня был только один SensorSample на каждый день, а значение его sample является средним значением sample всех объектов на этот день.Есть ли способ сделать это с помощью Streams?

Пока я получил это, чтобы сгруппировать их вместе:

Map<Long, List<SensorSample>> yearSamples = samples.stream()
                .collect(groupingBy(sample -> SECONDS_IN_A_DAY*Math.floorDiv(sample.getTimestamp(), SECONDS_IN_A_DAY)));

Но я не знаю, как идти дальше.

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

кажется, что вы хотите получить List<SensorSample> в результате, когда каждая группа после groupingBy сводится к единственному SensorSample.

List<SensorSample> result = samples.stream()
                .collect(groupingBy(sample -> SECONDS_IN_A_DAY*Math.floorDiv(sample.getTimestamp(), SECONDS_IN_A_DAY))
                .entrySet()
                .stream()
                .map(e -> {
                    SensorSample sensorSample = new SensorSample();
                    sensorSample.setTimestamp(e.getKey());
                    double average = e.getValue().stream()
                            .mapToDouble(SensorSample::getSample)
                            .average().orElse(0);
                    sensorSample.setSample(average);
                    sensorSample.setSensorType(e.getValue().get(0).getSensorType());
                    return sensorSample;
                }).collect(Collectors.toList());

Логика map выглядит немногонемного большой, поэтому я хотел бы рассмотреть вопрос об рефакторинге его для метода как такового:

private static SensorSample apply(Map.Entry<Long, List<SensorSample>> e) {
        SensorSample sensorSample = new SensorSample();
        sensorSample.setTimestamp(e.getKey());
        double average = e.getValue().stream()
                .mapToDouble(SensorSample::getSample)
                .average().orElse(0);
        sensorSample.setSample(average);
        sensorSample.setSensorType(e.getValue().get(0).getSensorType());
        return sensorSample;
}

Тогда конвейер потока станет:

List<SensorSample> result = samples.stream()
                .collect(groupingBy(sample -> SECONDS_IN_A_DAY*Math.floorDiv(sample.getTimestamp(), SECONDS_IN_A_DAY))
                .entrySet()
                .stream()
                .map(Main::apply)
                .collect(Collectors.toList());

Где Main - класс, содержащийapply метод.

0 голосов
/ 10 декабря 2018

Как-то так, я думаю.Чтобы определить среднее число в группе:

Map<Long, Double> averages = samples.stream()
  .collect(groupingBy(SensorSample::getTimestamp,
   averagingDouble(SensorSample::getSample)));

Я не расширил вашу формулу за день, я думаю, что это будет более читабельным, если я просто позвоню getTimestamp и опущу детали.Ваш код также может быть более читабельным, если вы добавите метод getDay в SensorSample.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...