Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java8Streams:从一个列表中查找符合根据另一个列表中的值计算的条件的项_Java_Java 8_Java Stream - Fatal编程技术网

Java8Streams:从一个列表中查找符合根据另一个列表中的值计算的条件的项

Java8Streams:从一个列表中查找符合根据另一个列表中的值计算的条件的项,java,java-8,java-stream,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 = ..; 我想知道是否有更简单的解决方案。公共列表查找匹配单击(列表CMP

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

class Click {
   long campaignId;
   Date date;
}

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

List<Click> clicks = ..;
List<Campaign> campaigns = ..;
我想知道是否有更简单的解决方案。

公共列表查找匹配单击(列表CMP,列表单击){
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));
}
列出cmpsProspective=cmps.stream().filter(cmp->“prospective”.equals(cmp.type)).collect(collector.toList()); 返回clicks.stream().filter(c->matchesAnyCmp(c,cmpsProspective).collect(collector.toList()); } 公共布尔匹配SANYCMP(单击,列出CMP){ 返回cmps.stream().anyMatch(单击->cmp.start.before(单击.date)和&cmp.end.after(单击.date)); }

替换getter字段,只需快速编写即可。

最突出的一点是,您的第二个要求与匹配无关,它只是
活动的一个条件。您必须测试这是否对您更有利:

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());
否则,我从未见过一个streams解决方案不涉及在第1个集合的谓词中对第2个集合进行流式处理,因此您所做的不会比您所做的更好。就可读性而言,如果您对它感到困惑,请创建一个测试布尔条件的方法并调用它:

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);
}
最后,提出两项不相关的建议:

  • 不要使用旧的
    Date
    类,而是使用新的
  • 如果
    活动
    类型
    只能有一些预定义的值(如“已提交”、“寻找客户”、“已接受”…),则
    枚举
    比一般的
    字符串
    更合适

  • 嗯,在我看来,有一个非常巧妙的方法来解决你的问题,霍尔格的原始想法(我会找到问题并将其链接到这里)

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

    static boolean checkClick(列出活动,单击){
    返回活动.stream().anyMatch(camp->camp.getCampaignId())
    ==单击.getCampaignId());
    }
    
    并定义一个绑定参数的函数:

    public static <T, U> Predicate<U> bind(BiFunction<T, U, Boolean> f, T t) {
        return u -> f.apply(t, u);
    }
    
    公共静态谓词绑定(双函数f,T){
    返回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());
    
    BiFunction-BiFunction=YourClass::checkClick;
    谓词=绑定(双功能,活动);
    clicks.stream()
    .filter(谓词::测试)
    .collect(Collectors.toList());
    
    我的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());
    
    Map Map=campeties.stream().filter(c->c.type.equals(“prospecting”))
    .collect(Collectors.groupingBy(c->c.campaiid));
    单击.stream().filter(k->map.containsKey(k.campaid))
    .filter(k->map.get(k.activitid).stream().anyMatch(c->c.start.before(k.date))和c.end.after(k.date)))
    .collect(Collectors.toList());
    
    该代码并不比原始代码短很多。但它会将性能从O(nm)提高到O(n+m),正如@Marco13在注释中提到的。如果您想缩短,请尝试

    Map Map=streamx.of(活动)
    .filter(c->c.type.equals(“prospecting”)).groupingBy(c->c.campaignId);
    StreamEx.of(clicks).filter(k->map.containsKey(k.campaid))
    .filter(k->map.get(k.activitid).stream().anyMatch(c->c.start.after(k.date)&&c.end.before(k.date)))
    .toList();
    
    我认为您真正需要做的就是将较大的lambda提取到一个命名方法中。@JoeC这真的有可能吗?这个lambda同时指单击和活动,这意味着我需要使用BiPredicate,而过滤器接受谓词
    单击->检查活动(单击,活动)
    不管(感知或真实)基于流的解决方案的优雅:我强烈建议使用一种算法不同的方法。
    anyMatch
    看起来很方便,但是有O(n),所以对于
    m
    点击和
    n
    活动,这是O(nm)。构建一个
    Map
    用作查找会将其更改为O(n+m)(可能有一些
    TreeMap
    支持按日期索引,但这可能是第二步)顺便说一句,麻烦的
    Date
    类已经被该类取代。尽可能避免
    Date
    ,但在必须转换的地方,您可以使用添加到旧类中的新方法进行转换:这确实很好,但是OP说他们发现当前的解决方案“混乱而复杂”,所以我无法想象这个解决方案会不那么复杂。
    BiFunction<List<Campaign>, Click, Boolean> biFunction = YourClass::checkClick;
    Predicate<Click> predicate = bind(biFunction, campaigns);
    
    clicks.stream()
          .filter(predicate::test)
          .collect(Collectors.toList());
    
    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());
    
    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();