Java Streams:将两个集合组合到一个映射中

时间:2016-03-21 16:17:42

标签: java java-8 java-stream

我有两个集合,一个仓库ID列表和一组小部件。小部件存在于不同数量的多个仓库中:

List<Long> warehouseIds;
List<Widget> widgets;

以下是类的定义示例:

public class Widget {
    public Collection<Stock> getStocks();
}

public class Stock {
    public Long getWarehouseId();
    public Integer getQuantity();
}

我想使用Streams API创建一个Map,其中仓库ID是密钥,值是特定仓库中数量最小的Widgets列表。因为多个小部件可以具有相同的数量,所以我们返回一个列表。

例如,仓库111 5 数量小工具A 5 小工具B ,以及小部件C 8

仓库222 0 数量小工具A 5 小工具B 5 Widget C 返回的地图将包含以下条目:

111 => ['WidgetA', 'WidgetB']

222 => ['WidgetA']

使用键开始设置Map似乎很容易,但我不知道如何构建下游缩减:

warehouseIds.stream().collect(Collectors.groupingBy(
    Function::Identity,
    HashMap::new,
    ???...

我认为我遇到的问题是根据库存仓库ID减少小部件,而不知道如何返回收集器来创建此小部件列表。以下是我目前如何获得特定仓库中库存最小的小部件列表(由 someWarehouseId 表示):

widgets.stream().collect(Collectors.groupingBy(
    (Widget w)->
        w.getStocks()
        //for a specific warehouse
        .stream().filter(stock->stock.getWarehouseId()==someWarehouseId)
        //Get the quantity of stocks for a widget
        .collect(Collectors.summingInt(Stock::getQuantity)),
    //Use a tree map so the keys are sorted
    TreeMap::new,
    //Get the first entry
    Collectors.toList())).firstEntry().getValue();

使用仓库清单上的forEach将其分成两个任务会使这项工作变得简单,但我想知道我是否可以在“单行”中执行此操作。

1 个答案:

答案 0 :(得分:4)

要解决此问题,我们需要使用比使用public class Values { public Values(int value) { this.SomeValue = value; } public int SomeValue { get; } } // Register as container.RegisterSingleton<Values>(() => new Values(1)); 更合适的方法来选择数量最小的值。

考虑以下方法:

  • 我们制作了TreeMap个初始小部件。我们需要对每个小部件的库存进行一些处理,但我们还需要保持小部件。让我Stream<Widget> flatMapStream<Widget>:新流将由我们拥有的每个Stream<Map.Entry<Stock, Widget>>组成,并包含相应的Stock
  • 我们会过滤这些元素,只保留Widget列表中包含Map.Entry<Stock, Widget>库存的warehouseId
  • 现在,我们需要根据每个warehouseIds的{​​{1}}对该流进行分组。因此,我们使用Collectors.groupingBy(classifier, downstream),其中分类器返回warehouseId
  • 下游收集器收集分类为相同密钥的元素。在这种情况下,对于归类为相同Stock的{​​{1}}元素,我们只需要保留库存数量最少的元素。没有内置的收集器,让我们使用MoreCollectors.minAll(comparator, downstream)库中的StreamEx。如果您不想使用该库,我已将其代码提取到此答案中并将使用它。
  • 比较器只是比较warehouseId中每只股票的数量。这样可以确保我们为固定Map.Entry<Stock, Widget>保留最低数量的元素。下游收集器用于减少收集的元素。在这种情况下,我们只想保留小部件,因此我们使用Collectors.mapping(mapper, downstream),映射器从warehouseId返回小部件,下游收集器收集到Collectors.toList()的列表。

示例代码:

Map.Entry<Stock, Widget>

使用以下warehouseId收集器:

Map.Entry<Stock, Widget>