优化大数据集的搜索技术

时间:2017-11-16 15:47:29

标签: java optimization

我目前正在开发一个项目,我需要使用大约300万行长的.csv文件和不同的.xlsx文件,其大小介于10行和1000行以上。我试图找到我的.xlsx文件和我的.csv文件中不同单元格之间的共性。 去做这个。我已经阅读了我的.csv文件和.xslx文件,并将它们存储在ArrayLists中。 我有我想要的工作,然而我正在使用的方法是O(n ^ 3)使用3嵌套for循环在每个之间进行搜索。

//This is our .xlsx file stored in an ArrayList
for(int i = 1; i<finalKnowledgeGraph.size(); i+=3) {
            //loop through our knowledgeGraph again
            for(int j = 1; j<finalKnowledgeGraph.size(); j+=3) {
                //loop through .csv file which is stored in an ArrayList
                for(int k=1; k<storeAsserions.size(); k++) {
                   if(finalKnowledgeGraph.get(i).equals(storeAsserions.get(k)) && finalKnowledgeGraph.get(j+1).equals(storeAsserions.get(k+1))){
                      System.out.println("Do Something");
                   } else if(finalKnowledgeGraph.get(i+1).equals(storeAsserions.get(k)) && finalKnowledgeGraph.get(j).equals(storeAsserions.get(k+1))) {
                       System.out.println("Do something else");
                   }
                }
            }
        }

在我的实际代码中,我的System.out.println("Do something")只是将每个文件的特定部分写入新的.csv文件。

现在,我正在做的事情是我的问题是优化。显然,如果我在数百万个输入上运行3个嵌套for循环,它将无法在我的生命周期内完成运行,所以我想知道我可以用什么方法来优化代码。

我的一位朋友建议将文件存储在内存中,因此读/写速度会快几倍。另一位朋友建议将文件存储在哈希表而不是ArrayLists中,以帮助加快进程,但由于我实际上是在搜索哈希表中的每个元素,所以我不知道这会如何加速进程。看起来它似乎将搜索从一个数据结构转移到另一个数据结构。但是我说我也会在这里发布问题,看看人们是否有任何关于我如何优化此代码的提示/建议。感谢

注意:我自己完全没有优化等知识,我发现其他关于S / O的问题对我在该领域的知识太具体了所以如果这个问题看似重复,我可能已经看到了你的问题'重新谈论已经无法理解内容

编辑:存储在两个ArrayLists中的所有东西都是动词:名词:名词对,我试图比较每个ArrayList之间的名词。由于我不关心动词,我开始在索引1处搜索。(仅针对某些情况)

1 个答案:

答案 0 :(得分:3)

一种可能的解决方案是使用数据库,在给定适当的索引的情况下,可以非常快速地进行搜索。假设数据适合内存,您可以更快。

原则

对于像

这样的问题
for (X x : xList) {
    for (Y y : yList) {
        if (x.someAttr() == y.someAttr()) doSomething(x, y);
    }
}

您只需根据

等属性将一个列表分区为存储桶
Map<A, List<Y>> yBuckets = new HashMap<>();
yList.forEach(y -> yBuckets.compute(y.someAttr(), (k, v) ->
    (v==null ? new ArrayList<>() : v).add(y));

现在,您迭代另一个列表,只查看正确存储桶中的元素,如

for (X x : xList) {
    List<Y> smallList = yBucket.get(x.someAttr());
    if (smallList != null) {
        for (Y y : smallList) {
            if (x.someAttr() == y.someAttr()) doSomething(x, y);
        }
    }
}

实际上可以省略比较,因为它总是正确的,但这不是重点。速度来自消除,以查看equals将返回错误的情况。

复杂性从二次线性减少到线性加上调用doSomething的次数。

您的案例

您的数据结构显然不合适。你将三胞胎变成一个列表,这是错误的。你肯定可以以某种方式解决它,但创建class Triplet {String verb, noun1, noun2}会使一切变得更简单。对于storeAsserions,看起来你正在使用对。它们似乎重叠,但这可能是一个错字,无论如何它并不重要。我们使用TripletPair s。

让我也重命名你的列表,以便代码更适合这个小窗口:

for (Triplet x : fList) {
    for (Triplet y : fList) {
        for (Pair z : sList) {
            if (x.noun1.equals(z.noun1) && y.noun2.equals(z.noun2)) {
                doSomething();
            } else if (x.noun2.equals(z.noun1) && y.noun1.equals(z.noun2)) {
                doSomethingElse();
            }
        }
    }
}

现在,我们需要在桶上进行一些循环,因此至少有一个equals测试始终为真,这样我们就可以节省处理非匹配数据的时间。让我们专注于第一个条件

x.noun1.equals(z.noun1) && y.noun2.equals(z.noun2)

我建议像

这样的循环
for (Pair z : sList) {
    for (Triplet x : smallListOfTripletsHavingNoun1SameAsZ) {
        for (Triplet y : smallListOfTripletsHavingNoun2SameAsZ) {
            doSomething();
        }
    }
}

小名单在第一部分得到计算。

没有比较过的非匹配条目,因此复杂性从立方体减少到匹配数量(=如果您编码的行将打印,则为数字。)

附录 - yBuckets

我们假设xList看起来像

[
  {id: 1, someAttr: "a"},
  {id: 2, someAttr: "a"},
  {id: 3, someAttr: "b"},
]

然后yBuckets

{
  "a": [
    {id: 1, someAttr: "a"},
    {id: 2, someAttr: "a"},
  ],
  :b": [
    {id: 3, someAttr: "b"},
  ],
}

一种简单的方法,如何创建这样的地图是

yList.forEach(y -> yBuckets.compute(y.someAttr(), (k, v) ->
   (v==null ? new ArrayList<>() : v).add(y));

以明文:

  • 来自y的每个yList
  • (k, v)
  • 的形式获取相应的地图条目
  • v为空时,则创建新的列表
  • 否则使用列表v
  • 无论如何,请向其添加y
  • 并将其存储回Map(除非在第三步中创建了新的List时,否则这是无操作。)