Java搜索两个阵列

时间:2017-03-16 19:25:07

标签: java algorithm performance arraylist

我有2个ArrayList' s。 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分钟的运行时间太长了。

1 个答案:

答案 0 :(得分:1)

我会很高兴看到一个良好而彻底的解决方案,同时我可以提出两个想法(或者两个转世一个)。

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

另一个选项是限制B类型的对象数量,对于A的每个对象,应该对其进行完全处理。构建一个逆N-gram索引:对于每个fullUrl,你取其长度为N的所有子串(“N-grams”),然后从每个这样的N-gram构建一个映射到一组具有这种N-gram的B克在他们的fullUrl。当搜索一个对象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密钥比较的必要性。