簡體   English   中英

(可選)在流上應用過濾器

[英]Optionally apply filter on stream

我正在嘗試在對象列表上執行“最佳匹配”。 我考慮實現一個級聯過濾器,目標是最終只實現一個最終成為“最佳匹配”的對象。 我有一個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和ObjectB可以切換,因此我不得不重復此代碼兩次,一次重復列出ObjectA,一次重復列出ObjectB。 我想做的是更多這樣的事情:

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);

現在,我可以像現在一樣在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;

或沒有流:

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;

這也可能會通用化以處理您的兩種情況:

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;
}

盡管謂詞看起來相同,但是假設ObjectAObjectB沒有定義這些屬性的公共基類(否則,這將太簡單了),它們在做不同的事情。 因此,這種重復是不可避免的。 試圖在謂詞中使用Function委托來使其更加通用化,不太可能使代碼更簡單。

這是您可以玩的把戲之一:

final ObjectA[] tmp= new ObjectA[1];
ObjectA match = listOfObjectA.stream()
        .filter(oA -> oB.getProp1().equals(oA.getProp1()))
        .peek(oA -> tmp[0] = tmp[0] == null ? oA : tmp[0])
        .filter(oA -> oB.getProp2().equals(oA.getProp2()))
        .peek(oA -> tmp[0] = oA)
        .filter(oA -> oB.getProp3().equals(oA.getProp3()))
        .findFirst().orElse(tmp[0]);

要么:

final ObjectA[] tmp= new ObjectA[2];
ObjectA match = listOfObjectA.stream()
        .filter(oA -> oB.getProp1().equals(oA.getProp1()))
        .peek(oA -> tmp[0] = oA)
        .filter(oA -> oB.getProp2().equals(oA.getProp2()))
        .peek(oA -> tmp[1] = oA)
        .filter(oA -> oB.getProp3().equals(oA.getProp3()))
        .findFirst().orElse(tmp[1] == null ? tmp[0] : tmp[1]);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM