使用Java Streams进行高效搜索

时间:2016-01-21 14:58:18

标签: performance java-8 java-stream

我有List个元素,其中没有可测量的顺序。它们的属性也很复杂,我不能简单地将它们插入到Set中(因为不同的属性可能代表相同的元素)。

通过我的程序,我分析列表中的每个元素,并在此基础上添加其他元素(如构建图形并转到每个节点以添加其他路径和节点)。但是,他们添加的元素可能等同于List中的其他元素。在这种情况下,它们不会被添加,并且等效元素的属性已更改(假设为计数器)。

我一直在使用此代码来查找是否存在等效状态:

public static State stateAlreadyExists(State current) {
    for (State any : list) {
        if (equivalencyMethod(any, current)) {
            return any;
        }
     }
     return null;
}

但是,这个代码尽管有O(n)的复杂性,但对我的情况来说还不够。我添加的每个元素都将执行此代码,并且每个分析元素添加约sqrt(N)个元素(因此,分析元素400正在创建20个新元素,例如)。为了提高性能,我使用了Java的并行流:

public static State stateAlreadyExists(State current) {
    Optional<State> opt = list.parallelStream().filter(
        any -> equivalencyMethod(any, current)).findFirst();
    if (opt.isPresent()) {
        return opt.get();
    }
    return null;
}

性能显着提高。 问题是这段代码并不是真正等效,因为我们在返回元素之前分析整个流。大多数情况下,等效元素位于列表的第一个sqrt(N)元素中,因此在第一个匹配时停止的方法会更好。

我知道streams有一种noneFound()方法。一找到匹配就会返回。但是,它返回boolean,而不是元素本身。有没有办法使用这个或类似构建的方法来返回找到的第一个匹配项?

根据JavaDoc,findFirst():

  

返回此流的第一个元素

findAny():

  

选择流中的任何元素。这是为了在并行操作中实现最大性能。

因此,通过使用findAny()调用,我的代码可以变得更加高效,因为顺序对我的问题并不重要,因为任何时候只有1个等效元素。

2 个答案:

答案 0 :(得分:2)

findFirst是一个短路终端操作(感谢Keppil)。

public static void main(String[] args) {
  final AtomicInteger countNew = new AtomicInteger();
  final AtomicInteger countDoStuff = new AtomicInteger();
  class A {
    A() { countNew.getAndIncrement(); }
    public boolean doStuff() { return countDoStuff.getAndIncrement() % 3 == 2; }
  }
  Stream.generate(A::new).limit(20).filter(A::doStuff).findFirst();
  System.out.println("Number of times an A was created: " + countNew);
  System.out.println("Number of times doStuff was called: " + countDoStuff);
}

此代码将打印

  

创建A的次数:3

     

调用doStuff的次数:3

但不是

  

创建A的次数:20

     

调用doStuff的次数:20

甚至更少

  

创建A的次数:20

     

调用doStuff的次数:3

答案 1 :(得分:1)

我建议你采用另一种方法。尝试以某种方式制定您的等效标准,以便您可以创建一个散列函数,该函数可以显着减少您必须执行的成对等价检查的数量。对于散列函数,如果两个不相等的项具有相同的散列,则没有问题,唯一重要的是两个相等的项具有相同的散列。然后,您只需将元素存储在HashSet中,这将为您完成繁重的工作,您只需要实现元素的equals(Object other)hashCode()

如果无法找到合适的哈希码,您仍然可以考虑是否能够排序您的对象,也就是说您可以制定一个比较函数,该函数可以告诉您任何两个对象的顺序对象(或它们是相等的)。然后你可以使用TreeSet和你的自定义比较器,这也可以超快速地工作。