To solve this problem, we need to use a more correct approach than using TreeMap to select the values โโwith the smallest values.
Consider the following approach:
- We make the
Stream<Widget> our original widgets. We will need to process the stocks of each widget, but we will also need to maintain the widget. Let flatMap that Stream<Widget> in Stream<Map.Entry<Stock, Widget>> : the new stream will consist of every Stock that we have, with its corresponding Widget . - We filter these items only for storage
Map.Entry<Stock, Widget> , where the stock has a warehouseId contained in the warehouseIds list. - Now we need to group this stream according to the
warehouseId each Stock . Therefore, we use Collectors.groupingBy(classifier, downstream) , where the classifier returns this warehouseId . - The downstream collector collects items that are classified into the same key. In this case, for the
Map.Entry<Stock, Widget> elements that were assigned to the same warehouseId , we need to save only those where the stock has the smallest amount. There are no built-in collectors for this, use MoreCollectors.minAll(comparator, downstream) from StreamEx . If you prefer not to use the library, I have extracted its code in this answer and will use it. - The comparator simply compares the amount of each stock in
Map.Entry<Stock, Widget> . This ensures that we keep the items with the least amount for a fixed warehouseId . The lower collector is used to reduce assembled items. In this case, we want to save only the widget, so we use Collectors.mapping(mapper, downstream) , where mapper returns the widgets from Map.Entry<Stock, Widget> and the downstream collectors are collected into a list with Collectors.toList() .
Code example:
Map<Long, List<Widget>> map = widgets.stream() .flatMap(w -> w.getStocks().stream().map(s -> new AbstractMap.SimpleEntry<>(s, w))) .filter(e -> warehouseIds.contains(e.getKey().getWarehouseId())) .collect(Collectors.groupingBy( e -> e.getKey().getWarehouseId(), minAll( Comparator.comparingInt(e -> e.getKey().getQuantity()), Collectors.mapping(e -> e.getValue(), Collectors.toList()) ) ));
with the following minAll collector:
public static <T, A, D> Collector<T, ?, D> minAll(Comparator<? super T> comparator, Collector<T, A, D> downstream) { return maxAll(comparator.reversed(), downstream); } public static <T, A, D> Collector<T, ?, D> maxAll(Comparator<? super T> comparator, Collector<? super T, A, D> downstream) { final class PairBox<U, V> { public U a; public V b; PairBox(U a, V b) { this.a = a; this.b = b; } } Supplier<A> downstreamSupplier = downstream.supplier(); BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); BinaryOperator<A> downstreamCombiner = downstream.combiner(); Supplier<PairBox<A, T>> supplier = () -> new PairBox<>(downstreamSupplier.get(), null); BiConsumer<PairBox<A, T>, T> accumulator = (acc, t) -> { if (acc.b == null) { downstreamAccumulator.accept(acc.a, t); acc.b = t; } else { int cmp = comparator.compare(t, acc.b); if (cmp > 0) { acc.a = downstreamSupplier.get(); acc.b = t; } if (cmp >= 0) downstreamAccumulator.accept(acc.a, t); } }; BinaryOperator<PairBox<A, T>> combiner = (acc1, acc2) -> { if (acc2.b == null) { return acc1; } if (acc1.b == null) { return acc2; } int cmp = comparator.compare(acc1.b, acc2.b); if (cmp > 0) { return acc1; } if (cmp < 0) { return acc2; } acc1.a = downstreamCombiner.apply(acc1.a, acc2.a); return acc1; }; Function<PairBox<A, T>, D> finisher = acc -> downstream.finisher().apply(acc.a); return Collector.of(supplier, accumulator, combiner, finisher); }