有两个类和两个相应的列表:
class Click {
long campaignId;
Date date;
}
class Campaign {
long campaignId;
Date start;
Date end;
String type;
}
List<Click> clicks = ..;
List<Campaign> campaigns = ..;
想要找到Click
中的所有clicks
:
在Campaign
列表中有相应的campaigns
,即Campaign
具有相同的campaignId
AND
此Campaign
有type
=&#34;预期&#34;以及
这Campaigns.start
&lt; click.date
&lt; 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());
我想知道这个问题是否有更简单的解决方案。
答案 0 :(得分:4)
我的2美分: 由于OP中没有太多的样板代码。因此,可能无法/必须减少代码中的行/字符。我们可以重写它以使其更清晰:
Map<Long, List<Campaign>> map = campaigns.stream().filter(c -> c.type.equals("prospecting"))
.collect(Collectors.groupingBy(c -> c.campaignId));
clicks.stream().filter(k -> map.containsKey(k.campaignId))
.filter(k -> map.get(k.campaignId).stream().anyMatch(c -> c.start.before(k.date) && c.end.after(k.date)))
.collect(Collectors.toList());
代码并不比原始代码短。但它会提高O(nm)到O(n + m)的性能,正如评论中提到的@ Marco13。如果您想要更短,请尝试StreamEx
Map<Long, List<Campaign>> map = StreamEx.of(campaigns)
.filter(c -> c.type.equals("prospecting")).groupingBy(c -> c.campaignId);
StreamEx.of(clicks).filter(k -> map.containsKey(k.campaignId))
.filter(k -> map.get(k.campaignId).stream().anyMatch(c -> c.start.after(k.date) && c.end.before(k.date)))
.toList();
答案 1 :(得分:3)
嗯,有一种非常巧妙的方法来解决你的问题IMO,来自Holger的原创想法(我会在这里找到问题并链接它)。
您可以定义执行检查的方法(我已将其简化了一点):
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());
用法是:
lein deps
答案 2 :(得分: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个无关的建议:
Date
课程,而是使用新的java.time API LocalDate。Campaign
&#39; type
只能有一些预定义的值(例如&#34;提交&#34;,&#34;探索&#34;,&#34;接受& #34; ...)然后enum
比普通String
更合适。答案 3 :(得分:1)
public List<Click> findMatchingClicks(List<Campaign> cmps, List<Click> clicks) {
List<Campaign> cmpsProspective = cmps.stream().filter(cmp -> "prospective".equals(cmp.type)).collect(Collectors.toList());
return clicks.stream().filter(c -> matchesAnyCmp(c, cmpsProspective).collect(Collectors.toList());
}
public boolean matchesAnyCmp(Click click, List<Campaign> cmps) {
return cmps.stream().anyMatch(click -> cmp.start.before(click.date) && cmp.end.after(click.date));
}
替换getter的字段,只需快速写下来。