什么是Java中最快的方式来形成与谓词集合匹配的对象集合?

时间:2017-06-09 19:43:59

标签: java multithreading parallel-processing java-8 predicate

考虑一组对象和一组谓词,它们是形成谓词对象对集合的最快方法,其中每一对都是一个对象,一个谓词返回true。

对象在对中必须是唯一的,但这不适用于谓词。

即。考虑对象A,B和C,以及谓词P1,P2,P3

然而,

(A,P1),(B,P1),(C,P2)是有效的一对对 (A,P1),(A,P1),(C,P2)无效,因为对中有重复的对象

因此,一旦谓词与某个对象匹配,它就会有效地拥有它。

即。鉴于上述限制,最快的方法是实现下面的方法:

Collection<Pair<Object,Predicate<Object>> getAllMatches(Collection<Object> objects, Collection<Predicate<Object>>);

其中Pair是:

class Pair<A,B> {
    A a;
    B b;
}

我知道我需要使用多线程,但我不确定要使用的最佳策略或最佳集合实现。此外,我认为由于需要某种锁定或所有权机制,唯一性约束将引入争用。

继承我的尝试,似乎是基本的,肯定必须有更快的方式:

  Collection<Pair<Object,Predicate> getAllMatches(BlockingQueue<Object> objects, Collection<Predicate> predicates){
List<Callable<Pair>> callables = new ArrayList<>();
for (Object o : objects){
    Callable<Pair> c = ()-> {
        Object polled = objects.take();
        for (Predicate p : predicates){
            if (p.test(polled)){
                return new Pair<Object,Predicate>(o,p);
            }
        }
        objects.put(o);
        return null;
    }
    callables.add(c);
}
List<Future<Pair>> futurePairs = exectors.invokeAll(callables);

 // return pairs
}

3 个答案:

答案 0 :(得分:3)

您的基准应该绝对是:

final Collection<Pair<Object, Predicate>> getMatches(
        final Collection<Object> objects, 
        final Collection<Predicate> predicates) {
    final Set<Pair<Object, Predicate>> matches = new HashSet<>();
    for (Object o : objects) {
        for (Predicate p : predicates) {
            if (p.test(o)) {
                matches.add(Pair.with(o, p));
                break;
            }
        }
    }
    return matches;
}

顺序执行通常最快。这看似违反直觉 - 在多个内核上运行测试应该最快 - 但对于许多操作而言,您的瓶颈实际上是内存访问。每个处理器都会停止运行,什么都不做,同时验证它的处理器级缓存是否与所有其他处理器的缓存一致。

如果您确信多线程可以为您节省一些时间,我建议测试这样的事情:

final Collection<Pair<Object, Predicate>> getMatches(
        final Collection<Object> objects, 
        final Collection<Predicate> predicates) {
    final List<Future<Pair<Object,Predicate>>> futures = new ArrayList<>();
    for (final Object o : objects) {
        futures.add(executorService.invoke(() -> {
            for (Predicate p : predicates) {
                if (p.test(o)) {
                    return Pair.with(o, p);
                }
            }
            return null;
        });
    }
    final Collection<Pair<Object,Predicate>> matches = new ArrayList<>(futures.size());
    for (final Future<Pair<Object,Predicate>> future : futures) {
        final Pair<Object,Predicate> pair = future.get();
        if (pair != null) {
            matches.add(pair);
        }
    }
    return matches;
}

没有任何线程到共享内存,所以没有锁争用担心。

答案 1 :(得分:1)

我想你可能会有点过分复杂。流对象和每个对象都会找到与之匹配的谓词:

objects.stream()    // or parallelStream() for multithreaded
    .distinct()     // can omit this if uniqueness of objects is enforced elsewhere
    .flatMap(obj -> predicates.stream()
        .filter(p -> p.test(obj))
        .map(p -> new Pair<>(obj, p))
        .limit(1)   // one predicate per object
    ).collect(toList());

答案 2 :(得分:0)

使用声明性并行性的Java8流的简短解决方案:

Collection<Pair<Object, Predicate<Object>>> getAllMatches(Set<Object> objects, Set<Predicate<Object>> predicates) {
    List<Pair<Object, Predicate<Object>>> pairs = predicates.parallelStream()
        .map(predicate -> new Pair<>(objects.stream().filter(predicate), predicate))
        .flatMap(pair -> pair.a.map(a -> new Pair<>(a, pair.b)))
        .collect(toList());
    return pairs;
}

并行执行由流管理:

    List<Pair<Object, Predicate<Object>>> pairs = predicates.parallelStream()

下一行创建一个Pair(该流包含所有匹配的对象):

        .map(predicate -> new Pair<>(objects.stream().filter(predicate), predicate))

下一行展平为配对:

        .flatMap(pair -> pair.a.map(a -> new Pair<>(a, pair.b)))

最后一行创建最终集合:

        .collect(toList());

如果objectspredicates不重复,请将.distinct()置于stream()parallelStream()之后。