У меня есть список объектов, который мне нужно сгруппировать по 2 различным атрибутам, а затем суммировать значения атрибута, структура моего объекта примерно такая:
private Long id1;
private Long id2;
private Double amountReserved;
private Double amountRequired;
//... more atributes and getters and setters
Итак, у меня есть список, например:
List<MyList> list = Arrays.asList(
list(1, A, 50, 200)
list(1, A, 50, 200)
list(1, B, 0, 100)
list(2, A, 10, 15)
list(2, A, 5, 15)
list(3, A, 0, 25));
То, что я пытаюсь достичь, - это новый список со следующей структурой:
list(1, A, 100, 100)
list(1, B, 0, 100)
list(2, A, 15, 0)
list(3, A, 0, 25)
Выяснение того, что является необходимым, я пытаюсь достичь:
amountReserved сгруппированного объектаamountRequired из суммированного amountReservedЧто у меня есть до сих пор:
Это дало мне группы, как я хотел
Map<Long, Map<String, List<MyList>>> map = null;
map = lista.stream().collect(Collectors.groupingBy(PreSeparacaoDto::getCodigoPedido,
Collectors.groupingBy(PreSeparacaoDto::getCodigoProduto)));
Этот суммируется по группе id1, но я изо всех сил пытаюсь добавить к ней вторую группу, так как получаю синтаксические ошибки:
lista.stream()
.collect(Collectors.groupingBy(PreSeparacaoDto::getCodigoPedido,
Collectors.summingDouble(PreSeparacaoDto::getProdutoQuantidadeSeparada)))
.forEach((codigoPedido, ProdutoQuantidadeSeparada) -> System.out.println( codigoPedido + ": " + ProdutoQuantidadeSeparada ));
Моя проблема в том, что я не смог собрать их вместе (в соответствии с требованием 2) и даже не был близок к достижению моего требования 3.
Я пытался использовать сокращение, как объяснил здесь , но, честно говоря, мне не удалось воспроизвести его с одной группировкой, сокращение возвращает ошибку, сообщающую, что мои параметры не соответствуют параметрам reducing. Я искал другие варианты здесь, на stackoverflow и других сайтах, но безуспешно.
Может ли кто-нибудь помочь мне и указать, где я не могу объединить сокращение с моей группой, или если это правильный путь, которому я должен следовать.
Хороший вопрос, забыл уточнить, что amountRequired является общим для всех элементов списка с одним и тем же id1 и id2.




Вы можете выполнить потоковую передачу по списку ввода дважды.
В первый раз вы группируете по id1, id2 и вычисляете сумму зарезервированного количества. Во второй раз вы можете снова передать список, сгруппировать его (по id1 и id2), используя приведенный выше результат, чтобы найти разницу.
Map<Long, Map<Long, Double>> amountReservedGroup = list.stream()
.collect(Collectors.groupingBy(MyList::getId1, Collectors.groupingBy(MyList::getId2,
Collectors.summingDouble(MyList::getAmountReserved))));
Map<Long, Map<Long, List<MyList>>> finalResult = list.stream()
.collect(Collectors.groupingBy(MyList::getId1, Collectors.groupingBy(MyList::getId2,
Collectors.mapping(o -> new MyList(o.getId1(), o.getId2(),
amountReservedGroup.get(o.getId1()).get(o.getId2()),
o.getAmountRequired() - amountReservedGroup.get(o.getId1()).get(o.getId2())),
Collectors.toList()))));
Примечание: Это не относится к случаю, когда результат вычитания отрицательный!!
Как указал nullpointer@ в комментариях, будет ли значение amountRequired одинаковым для заданных id1 и id2?
Думаю, проще всего использовать Collectors.grouping : вы говорите ему, как группировать и что собирать.
Вот пример вычисления только суммы AmountReserved :
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class GroupedSums {
static class MyList {
Long id1;
char id2;
Double amountReserved;
Double amountRequired;
public Long getId1() {
return id1;
}
public char getId2() {
return id2;
}
public Double getAmountReserved() {
return amountReserved;
}
public Double getAmountRequired() {
return amountRequired;
}
public MyList(Long id1, char id2, Double amountReserved, Double amountRequired) {
super();
this.id1 = id1;
this.id2 = id2;
this.amountReserved = amountReserved;
this.amountRequired = amountRequired;
}
Key key() {
return new Key(id1, id2);
}
}
private static MyList list(Long id1, char id2, Double amountReserved, Double amountRequired) {
return new MyList(id1, id2, amountReserved, amountRequired);
}
public GroupedSums() {
}
private static class Key {
Long id1;
char id2;
public Long getId1() {
return id1;
}
public char getId2() {
return id2;
}
public Key(Long id1, char id2) {
super();
this.id1 = id1;
this.id2 = id2;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id1 == null) ? 0 : id1.hashCode());
result = prime * result + id2;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Key other = (Key) obj;
if (id1 == null) {
if (other.id1 != null)
return false;
} else if (!id1.equals(other.id1))
return false;
if (id2 != other.id2)
return false;
return true;
}
@Override
public String toString() {
return "[id1 = " + id1 + ", id2 = " + id2 + "]";
}
}
public static void main(String[] args) {
List<MyList> list = Arrays.asList(
list(1L, 'A', 50d, 200d),
list(1L, 'A', 50d, 200d),
list(1L, 'B', 0d, 100d),
list(2L, 'A', 10d, 15d),
list(2L, 'A', 5d, 15d),
list(3L, 'A', 0d, 25d));
list.stream().collect(Collectors.groupingBy(MyList::key, Collectors.summingDouble(MyList::getAmountReserved)))
.forEach((k,v)->System.out.println("" + k + " :" + v));
}
}
ХТХ!
Возможно, вы просто ищете просто Collectors.toMap как:
List<MyList> output = new ArrayList<>(lista.stream()
.collect(Collectors.toMap(a -> a.getId1() + "-" + a.getId2(), a -> a, (myList1, myList2) -> {
myList1.amountReserved = myList1.amountReserved + myList2.amountReserved;
myList1.amountRequired = myList1.amountRequired - myList1.amountReserved;
return myList1;
})).values());
Примечание. Вы также можете обновить mergeFunction на основе ответа на этот комментарий. Это больше похоже на подход к выбору лучшего ключа на карте, а затем, возможно, к поиску списка значений.
Я бы не стал объединять два целочисленных типа, так как результирующая строка может столкнуться с другой конкатенацией двух целых чисел, то есть 2 + 35 равно 23 + 5.
@MCEmperor Ну, идея заключалась в том, чтобы фактически создать из этого функцию. (как для первого, так и для второго идентификатора). Хотя согласен с тем фактом, что реализация, которой я поделился здесь, ошибочна в случаях, подобных тем, которыми вы поделились.
Итак, вы объединяете id1 и id2, поэтому нет необходимости создавать «группу группы», и, используя «-», вы избегаете случая, упомянутого Императором. Я просто потерялся на a -> a, (myList1, myList2) ->. Я действительно не понимаю, что происходит с этой функцией стрелки, не могли бы вы указать мне некоторую информацию, чтобы я мог понять, что там происходит? Признаюсь, мне трудно понять, что именно мне нужно для поиска в Google...
Просто примечание: amountRequired не вычисляется правильно внутри функции. По какой-то причине, когда нужно выполнить группировку, вычисления не работают, поэтому я запустил эту часть кода вне потока, снова перебирая список, используя простой foreach. Как и все остальные, ваш код решил мою проблему. Спасибо.
вы можете сделать заказ по id1, а затем заказать id2 (чтобы убедиться, что элементы одного и того же списка и подсписка находятся друг за другом), а затем вы делаете вложенный foreach (перед тем, как вы итерируете подсписок, вы устанавливаете result_reserved_amount в 0 и result_required_amount в начальное значение ) то вы делаете это, если одинаковые идентификаторы (если id1 = предыдущее_id1 и id2 = предыдущее_id2) делают result_reserved_amount+= current_reserved_amount и result_required_amount -= current_reserved_amount, в противном случае обновите предыдущее_id1, предыдущее_id2, result_reserved_amount, result_required_amount
Спасибо за попытку помочь. Просто вопрос требует решения с использованием java lambda и stream. Хотя ваше предложение, вероятно, сработает, оно не соответствует требованиям приемлемого ответа. Тем не менее, спасибо за то, что написали возможное решение.
Есть ли определенность в отношении вычесть
amountRequiredиз суммированногоamountReserved, чтобы суммаRequired всегда была одинаковой для таких общих записей? Например,list(2, A, 10, 15) list(2, A, 5, 15), я думаю, вы вычли 15 из 15, и на выходе получилосьlist(2, A, 15, 0)... Но что, если на входе былоlist(2, A, 10, 15) list(2, A, 5, 30)?