Потоковые групповые входящие события сортировки

У меня есть серия событий, каждое событие содержит метку времени, устройство, серийный номер и измерение.

class Event {
    private String device;
    private String description;
    private String serialnumber;
    private Measurement measurement;
}

class Measurement {
    private LocalDateTime timestamp;
    private int value;
}

У меня есть поток этих событий, и я хотел бы объединить их в более простую структуру, отбросив серийный номер, затем сгруппировав их по устройствам, а затем отсортировав измерения по отметке времени и значению.

{device: "device_1", description: "first device", serialnumber: "1", measurement: { timestamp: 2022-04-23T18:20:22Z, value: 180}}
{device: "device_2", description: "second device", serialnumber: "2", measurement: { timestamp: 2022-04-23T18:20:28Z, value: 120}}
{device: "device_2", description: "second device", serialnumber: "2", measurement: { timestamp: 2022-04-23T18:20:20Z, value: 160}}
{device: "device_1", description: "first device", serialnumber: "1", measurement: { timestamp: 2022-04-23T18:20:22Z, value: 170}}
[
    {
        device: "device_1",
        description: "first device",
        measurements: [
            { timestamp: 2022-04-23T18:20:22Z, value: 170},
            { timestamp: 2022-04-23T18:20:22Z, value: 180}
        ]
    },
    {
        device: "device_2",
        description: "second device",
        measurements: [
            { timestamp: 2022-04-23T18:20:20Z, value: 160},
            { timestamp: 2022-04-23T18:20:28Z, value: 120}
        ]
    }
]

Мне удалось получить требуемый формат, создав класс "строитель", в который вы можете вставлять события, которые затем обрабатываются и добавляются к членам данных в правильном формате/порядке. Однако, я думаю, было бы лучше как-то добиться этого на лету без очередного лишнего класса, а с помощью потоковых методов groupingBy и toMap (и других?).

Должны ли отношения между вашими классами быть один к одному, а не один ко многим?

mamadaliev 23.04.2022 16:28
Основы программирования на Java
Основы программирования на Java
Java - это высокоуровневый объектно-ориентированный язык программирования, основанный на классах.
Концепции JavaScript, которые вы должны знать как JS программист!
Концепции JavaScript, которые вы должны знать как JS программист!
JavaScript (Js) - это язык программирования, объединяющий HTML и CSS с одной из основных технологий Всемирной паутины. Более 97% веб-сайтов используют...
1
1
69
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете получить Map<String, List<Measurement>>, используя groupingBy, если это то, что вы ищете:

Map<String, List<Measurement>> result = 
        events.stream()
                .collect(Collectors.groupingBy(Event::getDevice,
                         Collectors.mapping(Event::getMeasurement, Collectors.toList())));

Спасибо, это действительно то, что я пытаюсь, однако это все равно будет включать серийный номер и не соблюдает порядок сортировки, верно? Вот где у меня возникают проблемы с объединением отдельных шагов в один рабочий поток?

anon 23.04.2022 15:17
Ответ принят как подходящий

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

Я понимаю, что это может быть ограничением, но поскольку я не вижу способов сортировки подмножества, собранного с помощью Collectors, я решил использовать TreeSet для хранения сгруппированных измерений, чтобы они были отсортированы в соответствии с их естественным порядком.

class Event {
    private String device;
    private String description;
    private String serialnumber;
    private Measurement measurement;

    public Event(String device, String description, String serialnumber, Measurement measurement) {
        this.device = device;
        this.description = description;
        this.serialnumber = serialnumber;
        this.measurement = measurement;
    }

    //... getters and setters ...
}

class Measurement implements Comparable<Measurement> {
    private LocalDateTime timestamp;
    private int value;

    public Measurement(LocalDateTime timestamp, int value) {
        this.timestamp = timestamp;
        this.value = value;
    }

    //... getters and setters ...

    //Redefinition of the natural ordering by timestamp and value

    @Override
    public int compareTo(Measurement m) {
        Comparator<Measurement> cmp = Comparator.comparing(Measurement::getTimestamp).thenComparing(Measurement::getValue);
        return cmp.compare(this, m);
    }

    public String toString(){
        return String.format("%s - %d", timestamp, value);
    }
}

public class Test {

    public static void main(String[] args) {
        List<Event> dataSource = new ArrayList<>(List.of(
                new Event("device_1", "first device", "1", new Measurement(LocalDateTime.parse("2022-04-23T18:20:22"), 180)),
                new Event("device_2", "second device", "2", new Measurement(LocalDateTime.parse("2022-04-23T18:20:28"), 120)),
                new Event("device_2", "second device", "2", new Measurement(LocalDateTime.parse("2022-04-23T18:20:20"), 160)),
                new Event("device_1", "first device", "1", new Measurement(LocalDateTime.parse("2022-04-23T18:20:22"), 170))));

        Map<String, TreeSet<Measurement>> map = dataSource.stream()
                .collect(Collectors.groupingBy(Event::getDevice, Collectors.mapping(Event::getMeasurement, Collectors.toCollection(TreeSet::new))));

        for (String key: map.keySet()){
            System.out.printf("%s => %s%n", key, map.get(key));
        }
    }
}

Спасибо! Единственное, что я все еще пытаюсь добавить, это поле описания рядом с устройством. Любые идеи по этому поводу?

anon 23.04.2022 17:01

@picklepick означает ли это, что вы хотите сгруппировать по «устройство + описание»? Итак, как объединить устройство и описание с разделительной строкой, такой как «:» или «;»? К сожалению, операция groupingBy позволяет вам группировать только по методу элемента, который вы проходите через поток. Это означает, что вам нужно определить метод getDevicePlusDescr(), в котором вы в основном возвращаете два соединенных поля, чтобы поток группировался по ним. Единственный код, который вам нужно будет изменить, это Collectors.groupingBy(Event::getDevicePlusDescr, ...), помимо новой реализации геттера.

Dan 23.04.2022 17:21

описание не должно быть частью сортировки, оно должно быть просто включено в объект и, следовательно, в конечный результат.

anon 23.04.2022 17:26

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

Dan 23.04.2022 17:27

@picklepick, когда вы говорите об объекте, что вы имеете в виду? Как часть ключа хэш-карты?

Dan 23.04.2022 17:28

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

anon 23.04.2022 17:31

Хорошо, но я не думаю, что вы хотите группировать по всем полям события, определенно не по измерению, иначе не будет никаких причин собирать разные измерения устройством события. Если вы хотите сгруппировать по событию, я думаю, вам нужно определить ключевые поля для создания групп (скажем, устройство, описание и серийный номер). Затем вы можете либо вернуть значение с тремя объединенными полями, либо вернуть настраиваемый объект (EventKey), состоящий только из полей, по которым вы хотите сгруппировать (все те же три поля).

Dan 23.04.2022 17:50

Другие вопросы по теме