Java 8 流:从一个列表中查找与基于另一个列表中的值计算的条件匹配的项目

2022-09-02 13:34:51

有两个类和两个对应的列表:

class Click {
   long campaignId;
   Date date;
}

class Campaign {
   long campaignId;
   Date start;
   Date end;
   String type;
}

List<Click> clicks = ..;
List<Campaign> campaigns = ..;

并希望找到所有 s 在那:Clickclicks

  1. 有一个对应的 in 列表,即具有相同的 ANDCampaigncampaignsCampaigncampaignId

  2. 这已经= “预期”和Campaigntype

  3. Campaigns.start < click.date < Campaigns.end

到目前为止,我有以下实现(这对我来说似乎令人困惑和复杂):

clicks.
        stream().
        filter(click -> campaigns.stream().anyMatch(
                campaign -> campaign.getCampaignType().equals("prospecting") &&
                        campaign.getCampaignId().equals(click.getCampaignId()) &&
                        campaign.getStart().after(click.getDate()) &&
                        campaign.getEnd().before(click.getDate()))).
        collect(toList());

我想知道是否有更简单的解决方案来解决这个问题。


答案 1

好吧,有一种非常巧妙的方法来解决您的问题IMO,原始想法来自Holger(我会找到问题并在此处链接)。

您可以定义执行检查的方法(我只是稍微简化了一下):

static boolean checkClick(List<Campaign> campaigns, Click click) {
    return campaigns.stream().anyMatch(camp -> camp.getCampaignId() 
               == click.getCampaignId());
}

并定义一个绑定参数的函数:

public static <T, U> Predicate<U> bind(BiFunction<T, U, Boolean> f, T t) {
    return u -> f.apply(t, u);
}

用法是:

BiFunction<List<Campaign>, Click, Boolean> biFunction = YourClass::checkClick;
Predicate<Click> predicate = bind(biFunction, campaigns);

clicks.stream()
      .filter(predicate::test)
      .collect(Collectors.toList());

答案 2

突出的一件事是,您的第二个要求与匹配无关,它只是一个条件。您必须测试这是否对您更好:campaigns

clicks.stream()
    .filter(click -> campaigns.stream()
        .filter(camp -> "prospecting".equals(camp.type))
        .anyMatch(camp -> 
            camp.campaignId == click.campaignId &&
            camp.end.after(click.date) &&
            camp.start.before(click.date)
        )
    )
    .collect(Collectors.toList());

否则,我从未见过不涉及在第一个谓词内流式传输第二个集合的流解决方案,因此您无法比您所做的好得多。就可读性而言,如果它看起来令人困惑,那么创建一个测试布尔条件的方法并调用它:

clicks.stream()
    .filter(click -> campaigns.stream()
        .filter(camp -> "pre".equals(camp.type))
        .anyMatch(camp -> accept(camp, click))
    )
    .collect(Collectors.toList());

static boolean accept(Campaign camp, Click click) {
    return camp.campaignId == click.campaignId &&
            camp.end.after(click.date) &&
            camp.start.before(click.date);
}

最后,2个不相关的建议:

  1. 不要使用旧类,而是使用新的 java.time APILocalDateDate
  2. 如果 的 只能有一些预定义的值(如 “已提交”、“正在探矿”、“已接受”...),则 与 一般值更适合 。CampaigntypeenumString

推荐