Java 可选地在流上应用过滤器

Java 可选地在流上应用过滤器,java,java-8,java-stream,Java,Java 8,Java Stream,我正在尝试对对象列表执行“最佳匹配”。我想实现一个级联过滤器,目标是只得到一个最终成为“最佳匹配”的对象。我有一个ObjectA列表,还有一个ObjectB,我正在比较它的属性。如果有多个元素,有没有一种方法可以选择性地对流应用过滤器 目前我已经实现了如下: List<ObjectA> listOfObjectA; ObjectB oB; List<ObjectA> matchedByProp1 = listOfObjectA.stream() .filt

我正在尝试对对象列表执行“最佳匹配”。我想实现一个级联过滤器,目标是只得到一个最终成为“最佳匹配”的对象。我有一个ObjectA列表,还有一个ObjectB,我正在比较它的属性。如果有多个元素,有没有一种方法可以选择性地对流应用过滤器

目前我已经实现了如下:

List<ObjectA> listOfObjectA;
ObjectB oB;
List<ObjectA> matchedByProp1 = listOfObjectA.stream()
        .filter(oA -> oB.getProp1().equals(oA.getProp1())).collect(Collectors.toList());
if (matchedByProp1.isEmpty()) {
    // If no objects match, then return null
    return null;
} else if (matchedByProp1.size() == 1) {
    // If one object matches prop1, this is easy
    return matchedByProp1.stream().findFirst().orElse(null);
}

// If more than one object is left, filter further by prop2
List<ObjectA> matchedByProp2 = matchedByProp1.stream()
        .filter(oA -> oB.getProp2().equals(oA.getProp2()))
        .collect(Collectors.toList());
if (matchedByProp2.isEmpty()) {
    // If further filtering is not successful, return one from the previous set
    return matchedByProp1.stream().findFirst().orElse(null);
} else if (matchedByProp2.size() == 1) {
    // If one object matches prop2, this is easy
    return matchedByProp2.stream().findFirst().orElse(null);
}

// If more than one object is left, filter further by prop3
List<ObjectA> matchedByProp3 = matchedByProp2.stream()
        .filter(oA -> oB.getProp3().equals(oA.getProp3()))
        .collect(Collectors.toList());
if (matchedByProp3.isEmpty()) {
    // If further filtering is not successful, return one from the previous set
    return matchedByProp2.stream().findFirst().orElse(null);
} else if (matchedByProp3.size() == 1) {
    // If one object matches prop3, this is easy
    return matchedByProp3.stream().findFirst().orElse(null);
}

// We still have too many options, just choose one
return matchedByProp3.stream().findFirst().orElse(null);
ObjectA match = listOfObjectA.stream()
    .filter(oA -> oB.getProp1().equals(oA.getProp1()))
    .optionallyFilter(oA -> oB.getProp2().equals(oA.getProp2()))
    .optionallyFilter(oA -> oB.getProp3().equals(oA.getProp3()))
    .getFirst().orElse(null);
我曾尝试按如下方式实现此方法,但遇到了一个问题,即我试图使用流两次

private class Matcher<T, U> {
    private final U u;
    private final Stream<T> stream;

    public Matcher(U u) {
        this.u = u;
        stream = Stream.empty();
    }

    public Matcher(U u, Stream<T> stream) {
        this.u = u;
        this.stream = stream;
    }

    public Matcher<T, U> from(Stream<T> stream) {
        return new Matcher<>(u, stream);
    }

    public Matcher<T, U> mustMatch(Function<T, Object> tProp, Function<U, Object> uProp) {
        return new Matcher<>(u, stream.filter(t -> tProp.apply(t).equals(uProp.apply(u))));
    }

    public Matcher<T, U> shouldMatch(Function<T, Object> tProp, Function<U, Object> uProp) {
        if (stream.filter(t -> tProp.apply(t).equals(uProp.apply(u))).count() > 0) {
            return new Matcher<>(stream.filter(t -> tProp.apply(t).equals(uProp.apply(u))));
        }
        return this;
    }

    public Optional<T> get() {
        return stream.findFirst();
    }
}

ObjectA match = new Matcher<ObjectA, ObjectB>(oB, listOfObjectA.stream())
    .mustMatch(ObjectA::getProp1, ObjectB::getProp1)
    .shouldMatch(ObjectA::getProp2, ObjectB::getProp2)
    .shouldMatch(ObjectA::getProp3, ObjectB::getProp3)
    .get().orElse(null);
私有类匹配器{
私立大学;
私有最终流;
公共匹配器(U){
这个。u=u;
stream=stream.empty();
}
公共匹配器(U,流){
这个。u=u;
this.stream=流;
}
来自的公共匹配器(流){
返回新匹配器(u,流);
}
公共匹配器mustMatch(函数tProp、函数uProp){
返回新的匹配器(u,stream.filter(t->tProp.apply(t).equals(uProp.apply(u)));
}
公共匹配器应匹配(函数tProp、函数uProp){
if(stream.filter(t->tProp.apply(t).equals(uProp.apply(u))).count()>0){
返回新的匹配器(stream.filter(t->tProp.apply(t).equals(uProp.apply(u));
}
归还这个;
}
公共可选get(){
返回stream.findFirst();
}
}
ObjectA match=新匹配器(oB,listOfObjectA.stream())
.mustMatch(ObjectA::getProp1,ObjectB::getProp1)
.shouldMatch(ObjectA::getProp2,ObjectB::getProp2)
.shouldMatch(ObjectA::getProp3,ObjectB::getProp3)
.get().orElse(null);

现在我可以像现在一样在Matcher类中使用列表收集器,但似乎只是为了一个简单的条件,将流收集到列表中并重新流化,这似乎是不必要的。有更好的方法吗?请注意,在不同的使用中,可能会有不同的属性。

据我所知,您的逻辑:

List<Predicate<ObjectA>> props = Arrays.asList(
    oA -> oB.getProp1().equals(oA.getProp1()),
    oA -> oB.getProp2().equals(oA.getProp2()),
    oA -> oB.getProp3().equals(oA.getProp3()));

ObjectA previousChoice = null;

for(Predicate<ObjectA> p: props) {
    listOfObjectA = listOfObjectA.stream().filter(p).collect(Collectors.toList());
    if(listOfObjectA.isEmpty()) return previousChoice;
    else {
        previousChoice = listOfObjectA.get(0);
        if(listOfObjectA.size() == 1) break;
    }
}
return previousChoice;
List props=Arrays.asList(
oA->oB.getProp1().equals(oA.getProp1()),
oA->oB.getProp2().equals(oA.getProp2()),
oA->oB.getProp3().equals(oA.getProp3());
ObjectA previousChoice=null;
for(谓词p:props){
listOfObjectA=listOfObjectA.stream().filter(p.collect)(Collectors.toList());
if(listOfObjectA.isEmpty())返回上一个选项;
否则{
previousChoice=ListofObject.get(0);
如果(listOfObjectA.size()==1)中断;
}
}
返回先前的选择;
或没有溪流:

listOfObjectA = new ArrayList<>(listOfObjectA);
ObjectA previousChoice = null;

for(Predicate<ObjectA> p: props) {
    listOfObjectA.removeIf(p.negate());
    if(listOfObjectA.isEmpty()) return previousChoice;
    else {
        previousChoice = listOfObjectA.get(0);
        if(listOfObjectA.size() == 1) break;
    }
}
return previousChoice;
listOfObjectA=newarraylist(listOfObjectA);
ObjectA previousChoice=null;
for(谓词p:props){
删除对象列表(p.negate());
if(listOfObjectA.isEmpty())返回上一个选项;
否则{
previousChoice=ListofObject.get(0);
如果(listOfObjectA.size()==1)中断;
}
}
返回先前的选择;
这也可以被泛化以处理两种情况:

static ObjectB get(List<ObjectB> list, ObjectA oA) {
    return get(list,
        oB -> oA.getProp1().equals(oB.getProp1()),
        oB -> oA.getProp2().equals(oB.getProp2()),
        oB -> oA.getProp3().equals(oB.getProp3()));
}
static ObjectA get(List<ObjectA> list, ObjectB oB) {
    return get(list,
        oA -> oB.getProp1().equals(oA.getProp1()),
        oA -> oB.getProp2().equals(oA.getProp2()),
        oA -> oB.getProp3().equals(oA.getProp3()));
}
static <T> T get(List<T> listOfT, Predicate<T>... props) {
    listOfT = new ArrayList<>(listOfT);
    T previousChoice = null;

    for(Predicate<T> p: props) {
        listOfT.removeIf(p.negate());
        if(listOfT.isEmpty()) return previousChoice;
        else {
            previousChoice = listOfT.get(0);
            if(listOfT.size() == 1) break;
        }
    }
    return previousChoice;
}
静态ObjectB获取(列表,ObjectA oA){
返回get(列表,
oB->oA.getProp1().equals(oB.getProp1()),
oB->oA.getProp2().equals(oB.getProp2()),
oB->oA.getProp3().equals(oB.getProp3());
}
静态ObjectA get(列表,ObjectB oB){
返回get(列表,
oA->oB.getProp1().equals(oA.getProp1()),
oA->oB.getProp2().equals(oA.getProp2()),
oA->oB.getProp3().equals(oA.getProp3());
}
静态T获取(列表ListSoft、谓词…道具){
ListSoft=新的ArrayList(ListSoft);
T previousChoice=null;
for(谓词p:props){
listOfT.removeIf(p.negate());
if(listOfT.isEmpty())返回previousChoice;
否则{
previousChoice=ListSoft.get(0);
如果(listSoft.size()==1)中断;
}
}
返回先前的选择;
}

虽然谓词看起来相同,但它们做的事情不同,假设
ObjectA
ObjectB
没有定义这些属性的公共基类(否则就太简单了)。因此,这种重复是不可避免的。尝试在谓词中使用
函数
委托对其进行更广泛的泛化,不太可能使代码更简单。

据我所知,您的逻辑:

List<Predicate<ObjectA>> props = Arrays.asList(
    oA -> oB.getProp1().equals(oA.getProp1()),
    oA -> oB.getProp2().equals(oA.getProp2()),
    oA -> oB.getProp3().equals(oA.getProp3()));

ObjectA previousChoice = null;

for(Predicate<ObjectA> p: props) {
    listOfObjectA = listOfObjectA.stream().filter(p).collect(Collectors.toList());
    if(listOfObjectA.isEmpty()) return previousChoice;
    else {
        previousChoice = listOfObjectA.get(0);
        if(listOfObjectA.size() == 1) break;
    }
}
return previousChoice;
List props=Arrays.asList(
oA->oB.getProp1().equals(oA.getProp1()),
oA->oB.getProp2().equals(oA.getProp2()),
oA->oB.getProp3().equals(oA.getProp3());
ObjectA previousChoice=null;
for(谓词p:props){
listOfObjectA=listOfObjectA.stream().filter(p.collect)(Collectors.toList());
if(listOfObjectA.isEmpty())返回上一个选项;
否则{
previousChoice=ListofObject.get(0);
如果(listOfObjectA.size()==1)中断;
}
}
返回先前的选择;
或没有溪流:

listOfObjectA = new ArrayList<>(listOfObjectA);
ObjectA previousChoice = null;

for(Predicate<ObjectA> p: props) {
    listOfObjectA.removeIf(p.negate());
    if(listOfObjectA.isEmpty()) return previousChoice;
    else {
        previousChoice = listOfObjectA.get(0);
        if(listOfObjectA.size() == 1) break;
    }
}
return previousChoice;
listOfObjectA=newarraylist(listOfObjectA);
ObjectA previousChoice=null;
for(谓词p:props){
删除对象列表(p.negate());
if(listOfObjectA.isEmpty())返回上一个选项;
否则{
previousChoice=ListofObject.get(0);
如果(listOfObjectA.size()==1)中断;
}
}
返回先前的选择;
这也可以被泛化以处理两种情况:

static ObjectB get(List<ObjectB> list, ObjectA oA) {
    return get(list,
        oB -> oA.getProp1().equals(oB.getProp1()),
        oB -> oA.getProp2().equals(oB.getProp2()),
        oB -> oA.getProp3().equals(oB.getProp3()));
}
static ObjectA get(List<ObjectA> list, ObjectB oB) {
    return get(list,
        oA -> oB.getProp1().equals(oA.getProp1()),
        oA -> oB.getProp2().equals(oA.getProp2()),
        oA -> oB.getProp3().equals(oA.getProp3()));
}
static <T> T get(List<T> listOfT, Predicate<T>... props) {
    listOfT = new ArrayList<>(listOfT);
    T previousChoice = null;

    for(Predicate<T> p: props) {
        listOfT.removeIf(p.negate());
        if(listOfT.isEmpty()) return previousChoice;
        else {
            previousChoice = listOfT.get(0);
            if(listOfT.size() == 1) break;
        }
    }
    return previousChoice;
}
静态ObjectB获取(列表,ObjectA oA){
返回get(列表,
oB->oA.getProp1().equals(oB.getProp1()),
oB->oA.getProp2().equals(oB.getProp2()),
oB->oA.getProp3().equals(oB.getProp3());
}
静态ObjectA get(列表,ObjectB oB){
返回get(列表,
oA->oB.getProp1().equals(oA.getProp1()),
oA->oB.getProp2().equals(oA.getProp2()),
oA->oB.getProp3().equals(oA.getProp3());
}
静态T获取(列表ListSoft、谓词…道具){
ListSoft=新的ArrayList(ListSoft);
T previousChoice=null;
for(谓词p:props){
listOfT.removeIf(p.negate());
if(listOfT.isEmpty())返回previousChoice;
否则{
previousChoice=ListSoft.get(0);