【问题标题】:Java Searching through two ArraysJava搜索两个数组
【发布时间】:2017-08-08 03:53:57
【问题描述】:

我有 2 个 ArrayList。 ArrayList A 有 8.1k 个元素,ArrayList B 有 81k 个元素。

我需要遍历B,在A 中搜索该特定项目,然后更改列表B 中匹配元素中的字段。

这是我的代码:

private void mapAtoB(List<A> aList, ListIterator<B> it) {
    AtomicInteger i = new AtomicInteger(-1);
    while(it.hasNext()) {
        System.out.print(i.incrementAndGet() + ", ");
        B b = it.next();
        aList.stream().filter(a -> b.equalsB(a)).forEach(a -> {
            b.setId(String.valueOf(a.getRedirectId()));
            it.set(b);
        });
    }
    System.out.println();
}

public class B {
    public boolean equalsB(A a) {
        if (a == null) return false;

        if (this.getFullURL().contains(a.getFirstName())) return true;

        return false;
    }
}

但这需要永远。要完成此方法,大约需要 15 分钟。有什么办法可以优化这些吗? 15 分钟的运行时间太长了。

【问题讨论】:

  • 使用索引,卢克!
  • 我将从删除 System.out.print 和 println 调用开始。这很可能是大部分时间。您还应该告诉 b.equalsB(a) 做了什么(即发布代码):您可以使用 HashMap,并将复杂度降低到 O(m) 而不是 O(m*n)。并删除 it.set(b),它自己替换 b,因此是不必要的。此外,由于每个匹配的 a 都会替换 B 中由先前匹配的 A 设置的 ID,因此您可以向后迭代,并在找到匹配项后立即停止循环。
  • @JBNizet 我确实发布了 b.equalsB(a) 的代码。它就在第一种方法的正下方。并且 B 需要设置,因为我们正在更改 id 然后将其放回列表中
  • Bs中的URL长什么样子?它们看起来都一样吗?你能解析它们并从中提取“名字”部分吗?
  • @JBNizet 它们各不相同,因此很难进行标准解析

标签: java algorithm performance arraylist


【解决方案1】:

我很高兴看到一个好的和彻底的解决方案,同时我可以提出两个想法(或者可能是一个的两个轮回)。

第一个是加快在 B 类型对象中搜索所有 A 类型对象。为此,Rabin-Karp 算法似乎适用且足够简单,可以快速实现,Aho-Corasick 更难但可能会更好结果,不知道好多少。

另一个选项是限制类型 B 的对象数量,这些对象应该为 A 的每个对象完全处理,例如,您可以构建一个逆 N-gram 索引:对于每个 fullUrl,您获取其所有长度为 N ("N-grams") 的子字符串,然后构建一个从每个这样的 N-gram 到一组 B 的映射,其中包含这样的 N-gram他们的完整网址。在搜索对象 A 时,您获取它的所有 N-gram,为每个这样的 N-gram 找到一组 B,并与所有这些集合相交,交集将包含您应该完全处理的所有 B。我快速实施了这种方法,对于您指定的尺寸,它为 N=4 提供了 6-7 时间的加速;随着 N 的增长,搜索变得更快,但构建索引会减慢(因此,如果您可以重用它,则最好选择更大的 N)。对于您指定的大小,该索引大约需要 200 Mb,因此这种方法只会随着 B 集合的增长而扩展。假设所有字符串都比NGRAM_LENGTH长,这里是使用Guava的SetMultimapHashMultimap构建索引的快速而肮脏的代码:

    SetMultimap<String, B> idx = HashMultimap.create();
    for (B b : bList) {
        for (int i = 0; i < b.getFullURL().length() - NGRAM_LENGTH + 1; i++) {
            idx.put(b.getFullURL().substring(i, i + NGRAM_LENGTH), b);
        }
    }

对于搜索:

private void mapAtoB(List<A> aList, SetMultimap<String, B> mmap) {
    for (A a : aList) {
        Collection<B> possible = null;
        for (int i = 0; i < a.getFirstName().length() - NGRAM_LENGTH + 1; i++) {
            String ngram = a.getFirstName().substring(i, i + NGRAM_LENGTH);
            Set<B> forNgram = mmap.get(ngram);
            if (possible == null) {
                possible = new ArrayList<>(forNgram);
            } else {
                possible.retainAll(forNgram);
            }
            if (possible.size() < 20) { // it's ok to scan through 20
                break;
            }
        }
        for (B b : possible) {
            if (b.equalsB(a)) {
                b.setId(a.getRedirectId());
            }
        }
    }
}

一个可能的优化方向是使用散列而不是完整的 N-gram,从而减少内存占用和 N-gram 键比较的必要性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多