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

And*_*sky 13 java java-8 java-stream

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

class Click {
   long campaignId;
   Date date;
}

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

List<Click> clicks = ..;
List<Campaign> campaigns = ..;
Run Code Online (Sandbox Code Playgroud)

并希望找到所有Clickclicks:

  1. 列表中有相应Campaigncampaigns,即Campaign具有相同的campaignIdAND

  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());
Run Code Online (Sandbox Code Playgroud)

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

use*_*551 6

突出的一件事是您的第二个要求与匹配无关,它只是一个条件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());
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 不要使用旧Date类,而是使用新的java.time APILocalDate
  2. 如果Campaign'stype只能有一些预定义的值(如“提交”、“展望”、“接受”...),那么 anenum将比一般 更适合String


Eug*_*ene 6

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

您可以定义执行检查的方法(我已经简化了一点):

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

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

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

用法是:

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

clicks.stream()
      .filter(predicate::test)
      .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)