Avoir deux classes et deux listes correspondantes:
class Click {
long campaignId;
Date date;
}
class Campaign {
long campaignId;
Date start;
Date end;
String type;
}
List<Click> clicks = ..;
List<Campaign> campaigns = ..;
Et je veux trouver tous les Click
s dans clicks
qui:
Avoir un Campaign
correspondant dans campaigns
liste, c’est-à-dire Campaign
avec le même campaignId
ET
Ce Campaign
a type
= "prospectif" ET
Cette Campaigns.start
<click.date
<Campaigns.end
Jusqu'à présent, j'ai la mise en oeuvre suivante (qui me semble confuse et complexe):
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());
Je me demande s'il existe une solution plus simple au problème.
Une chose qui se démarque, c'est que votre deuxième exigence n'a rien à voir avec l'appariement, c'est une condition sur campaigns
seulement. Vous devrez vérifier si cela vous convient mieux:
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());
Sinon, je n'ai jamais vu de solution de flux qui n'implique pas le streaming de la 2e collection à l'intérieur du prédicat du 1er, vous ne pouvez donc pas faire mieux que ce que vous avez fait. En termes de lisibilité, si cela vous semble déroutant, créez une méthode qui teste la condition booléenne et appelez-la:
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);
}
Enfin, 2 suggestions indépendantes:
Date
, utilisez plutôt la nouvelle API Java.timeLocalDate .Campaign
'type
ne peut avoir que des valeurs prédéfinies (comme "soumis", "prospection", "accepté" ...), alors un enum
conviendrait mieux que un général String
.Mes 2 centimes: Puisqu'il n'y a pas beaucoup de code passe-partout dans OP. Il peut donc être impossible/nécessaire de réduire les lignes/caractères dans les codes. nous pourrions le réécrire pour le rendre un peu plus clair:
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());
Le code n'est pas beaucoup plus court que le code original. mais cela améliorera les performances de O(nm) à O (n + m), comme @ Marco13 l’a mentionné dans les commentaires. Si vous voulez plus court, essayez 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();
Eh bien, il existe un moyen très astucieux de résoudre votre problème OMI, idée originale venant de Holger (je vais trouver la question et la relier ici).
Vous pouvez définir votre méthode qui effectue les vérifications (je l'ai simplifiée un peu):
static boolean checkClick(List<Campaign> campaigns, Click click) {
return campaigns.stream().anyMatch(camp -> camp.getCampaignId()
== click.getCampaignId());
}
Et définissez une fonction qui lie les paramètres:
public static <T, U> Predicate<U> bind(BiFunction<T, U, Boolean> f, T t) {
return u -> f.apply(t, u);
}
Et l'utilisation serait:
BiFunction<List<Campaign>, Click, Boolean> biFunction = YourClass::checkClick;
Predicate<Click> predicate = bind(biFunction, campaigns);
clicks.stream()
.filter(predicate::test)
.collect(Collectors.toList());
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));
}
Remplacez les champs pour les accesseurs, venez de l'écrire rapidement.