Если у вас есть специальный выделенный конструктор и методы hashCode
и equals
, последовательно реализованные в Foo
следующим образом:
public Foo(Foo that) { // not a copy constructor!!!
this.category = that.category;
this.amount = 0;
this.price = 0;
}
public int hashCode() {
return Objects.hashCode(category);
}
public boolean equals(Object another) {
if (another == this) return true;
if (!(another instanceof Foo)) return false;
Foo that = (Foo) another;
return Objects.equals(this.category, that.category);
}
Приведенные выше реализации hashCode
и equals
позволяют использовать Foo
в качестве значимого ключа на карте (в противном случае ваша карта будет повреждена).
Теперь, с помощью нового метода в Foo
, который выполняет агрегацию атрибутов amount
и price
одновременно, вы можете делать то, что вы хотите, в 2 этапа. Сначала метод:
public void aggregate(Foo that) {
this.amount += that.amount;
this.price += that.price;
}
Теперь окончательное решение:
Map<Foo, List<Foo>> result = fooList.stream().collect(
Collectors.collectingAndThen(
Collectors.groupingBy(Foo::new), // works: special ctor, hashCode & equals
m -> { m.forEach((k, v) -> v.forEach(k::aggregate)); return m; }));
РЕДАКТИРОВАТЬ: несколько замечаний отсутствовали ...
С одной стороны, это решение заставляет вас использовать реализацию hashCode
и equals
, которая считает два разных экземпляра Foo
равными, если они принадлежат одному и тому же category
. Возможно, это нежелательно, или у вас уже есть реализация, которая учитывает больше или другие атрибуты.
С другой стороны, использование Foo
в качестве ключа карты, которая используется для группировки экземпляров по одному из ее атрибутов, является довольно редким случаем использования. Я думаю, что было бы лучше просто использовать атрибут category
для группировки по категориям и иметь две карты: Map<String, List<Foo>>
для сохранения групп и Map<String, Foo>
для хранения агрегированных price
и amount
, причем ключ category
в обоих случаях.
Кроме того, это решение изменяет ключи карты после того, как в нее введены записи. Это опасно, потому что это может сломать карту. Однако здесь я изменяю только атрибуты Foo
, которые не участвуют ни в реализации hashCode
, ни equals
Foo
. Я думаю, что этот риск приемлем в этом случае из-за необычности требования.