Java функциональный подход для объединения идентичных элементов в один - PullRequest
1 голос
/ 17 февраля 2020

Я хотел бы использовать Streams API для обработки журнала вызовов и расчета общей суммы счетов за один и тот же номер телефона. Вот код, который достигает этого с помощью гибридного подхода, но я хотел бы использовать полностью функциональный подход:

List<CallLog> callLogs = Arrays.stream(S.split("\n"))
                    .map(CallLog::new)
                    .sorted(Comparator.comparingInt(callLog -> callLog.phoneNumber))
                    .collect(Collectors.toList());

            for (int i = 0; i< callLogs.size() -1 ;i++) {
                if (callLogs.get(i).phoneNumber == callLogs.get(i+1).phoneNumber) {
                    callLogs.get(i).billing += callLogs.get(i+1).billing;
                    callLogs.remove(i+1);
                }
            }

Ответы [ 5 ]

3 голосов
/ 17 февраля 2020

Вы можете использовать Collectors.groupingBy для группировки CallLog объекта по phoneNumber с Collectors.summingInt для суммирования счетов сгруппированных элементов

Map<Integer, Integer> likesPerType = Arrays.stream(S.split("\n"))
                                           .map(CallLog::new)
                 .collect(Collectors.groupingBy(CallLog::getPhoneNumber, Collectors.summingInt(CallLog::getBilling)));
1 голос
/ 17 февраля 2020
Map<Integer, Integer> result = Arrays.stream(S.split("\n"))
                    .map(CallLog::new)
                    .sorted(Comparator.comparingInt(callLog -> callLog.phoneNumber))
                    .collect(Collectors.toMap(
                        c -> c.phoneNumber(),
                        c -> c.billing(),
                        (a, b) -> a+b
                     ));

И если вы хотите иметь «List callLogs» в результате:

List<CallLog> callLogs = Arrays.stream(S.split("\n"))
                        .map(CallLog::new)
                        .collect(Collectors.toMap(
                            c -> c.phoneNumber(),
                            c -> c.billing(),
                            (a, b) -> a+b
                         ))
                        .entrySet()
                        .stream()
                        .map(entry -> toCallLog(entry.getKey(), entry.getValue()))
                        .sorted(Comparator.comparingInt(callLog -> callLog.phoneNumber))
                        .collect(Collectors.toList())
0 голосов
/ 18 февраля 2020

То, что вы пытаетесь сделать, это удалить дубликаты телефонных номеров при добавлении их биллинга. Единственное, с чем несовместимы потоки - это операции удаления. Так как же мы можем сделать то, что вам нужно, без удаления?

Что ж, вместо сортировки я бы go с groupingBy номерами телефонов, тогда я бы отобразил список групп callLogs в callLogs с помощью биллинг уже накоплен.

0 голосов
/ 17 февраля 2020

Вы можете сгруппировать сумму счета по phoneNumber, как сказал ВЛАЗ. Пример реализации может выглядеть примерно так:

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

public class Demo {

    public static void main(String[] args) {

        final String s = "555123456;12.00\n"
                + "555123456;3.00\n"
                + "555123457;1.00\n"
                + "555123457;2.00\n"
                + "555123457;5.00";

        final Map<Integer, Double> map = Arrays.stream(s.split("\n"))
                .map(CallLog::new)
                .collect(Collectors.groupingBy(CallLog::getPhoneNumber, Collectors.summingDouble(CallLog::getBilling)));

        map.forEach((key, value) -> System.out.printf("%d: %.2f\n", key, value));
    }

    private static class CallLog {

        private final int phoneNumber;
        private final double billing;

        public CallLog(int phoneNumber, double billing) {
            this.phoneNumber = phoneNumber;
            this.billing = billing;
        }

        public CallLog(String s) {
            final String[] strings = s.split(";");
            this.phoneNumber = Integer.parseInt(strings[0]);
            this.billing = Double.parseDouble(strings[1]);
        }

        public int getPhoneNumber() {
            return phoneNumber;
        }

        public double getBilling() {
            return billing;
        }
    }
}

, который выдает следующий вывод:

555123456: 15.00
555123457: 8.00
0 голосов
/ 17 февраля 2020

Вы можете сохранить сортировку -> коллекцию в список -> итерацию списка значений рядом друг с другом, если вместо этого выполните следующее

  1. Создание всех CallLog объектов.
  2. Объединить их по полю phoneNumber
    • объединять поля billing каждый раз
  3. Возвращать уже объединенные элементы

Это можно сделать с помощью Collectors.toMap (Function, Function, BinaryOperator) , где третий параметр - это функция слияния, которая определяет, как будут комбинироваться элементы с одинаковыми ключами:

Collection<CallLog> callLogs = Arrays.stream(S.split("\n"))
  .map(CallLog::new)
  .collect(Collectors.toMap( //a collector that will produce a map
    CallLog::phoneNumber,    //using phoneNumber as the key to group
    x -> x,                  //the item itself as the value
    (a, b) -> {              //and a merge function that returns an object with combined billing
      a.billing += b.billing;
      return a;
    }))
  .values(); //just return the values from that map

В итоге у вас будет CallLog предметов с уникальными phoneNumber полями, чье поле billing равно комбинации всех billing с ранее дублированных phoneNumber с.

...