如何使用流和1.​​8函数将2个列表结合在一起?

时间:2019-03-07 14:06:37

标签: java java-8 java-stream

我有List个价格和价格组。

static class PriceGroup {
    String priceName;
    String priceGroup;
}

static class Price {
    String priceName;
    Integer price;
}

下面是一些示例数据,我编写了一个实现,以找到每个PriceGroup的最低Price。有什么建议我可以重构它以利用流来加入这些数据吗?

List<PriceGroup> priceGroups = Arrays.asList(
        new PriceGroup("F1", "Friends"),
        new PriceGroup("F2", "Friends"),
        new PriceGroup("O1", "Others"),
        new PriceGroup("O2", "Others"));

List<Price> prices = Arrays.asList(
        new Price("F1", 100),
        new Price("F2", 150),
        new Price("O1", 250),
        new Price("O2", 300));

public Map<String, Integer> getBestPrices(List<PriceGroup> priceGroups, List<Price> prices) 
{
    Map<String, Integer> bestPrice = new HashMap<String, Integer>(); 
    for (PriceGroup priceGroup : priceGroups) {
        if (bestPrice.get(priceGroup.priceGroup) == null) {
            bestPrice.put(priceGroup.priceGroup, 10000000);
        }

        for (Price price : prices) {
            if (price.priceName.equals(priceGroup.priceName)) {
                bestPrice.put(priceGroup.priceGroup, Math.min(price.price, bestPrice.get(priceGroup.priceGroup)));
            }
        }
    }

    return bestPrice;
}

对于给定的数据,我的函数应返回具有以下内容的地图:

  

F1 => 100
  O1 => 250

3 个答案:

答案 0 :(得分:2)

要加入2个列表,您可以考虑创建专用对象:

class Joined {
    String priceGroup;
    String priceName;
    Integer price;
    ...

然后,您可以使用flatMappriceGroups字段上将prices联接到priceName并按priceGroup分组:

Map<String, Optional<Joined>> map = priceGroups.stream()
        .flatMap(group -> prices.stream()
                .filter(p -> p.getPriceName().equals(group.getPriceName()))
                .map(p -> new Joined(group.getPriceGroup(), group.getPriceName(), p.getPrice())))
        .collect(groupingBy(Joined::getPriceGroup, minBy(Comparator.comparing(Joined::getPrice))));

现在从地图获取值,您可以打印预期结果:

for (Optional<Joined> value : map.values()) {
        value.ifPresent(joined -> System.out.println(joined.getPriceName() + " " + joined.getPrice()));
    }

// O1 250
// F1 100

答案 1 :(得分:2)

首先:我认为@Ruslan的答案是您应该使用的答案。

但是您提到返回必须为Map<String, Integer>,并且字符串应为F1而不是Friends。因此,我尝试一次完成所有操作,并获得了该功能的可憎性:

public static Map<String, Integer> getBestPricesV2(List<PriceGroup> priceGroups, List<Price> prices) {
    final String unknownPriceName = "Unkown price name";

    return prices.stream().collect(Collectors.groupingBy(
            // map the price name the priceGroup name
            price -> priceGroups.stream()
                    .filter(priceGroup -> priceGroup.getPriceName().equals(price.getPriceName()))
                    .findFirst()
                    .map(PriceGroup::getPriceGroup)
                    .orElse(unknownPriceName), 
            Collectors.minBy(Comparator.comparing(Price::getPrice))))
        .entrySet()
        .stream()
        // extract the Optional<Price> to the price value
        .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().orElse(new Price(unknownPriceName, -1)).getPrice()));
}

答案 2 :(得分:2)

您可以简单地避免对此操作使用联接类,并按以下步骤执行:

public Map<String, Integer> getBestPrices(List<PriceGroup> priceGroups, List<Price> prices) {
// create a map of priceName to minimum price value using list of prices
    Map<String, Integer> minPrice = prices.stream()
            .collect(Collectors.groupingBy(Price::getPriceName,
                    Collectors.reducing(0, Price::getPrice,
                            BinaryOperator.minBy(Comparator.naturalOrder()))));

// use the map above to fill in the best prices map with values present or default
    return priceGroups.stream()
            .collect(Collectors.toMap(PriceGroup::getPriceGroup,
                    priceGroup ->
                            minPrice.getOrDefault(priceGroup.getPriceName(), 10000000)));
}