我目前正在进行一个项目,需要处理一个大约300万行长的.csv文件和大小在10行到1000行之间的不同.xlsx文件。我试图在.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上的其他问题对我在该领域的知识来说太具体了,所以如果这个问题看起来像是重复的,我可能已经看到了你谈论的问题,无法理解的内容
编辑:存储在两个ArrayList中的所有内容都是动词:名词:名词对,我试图比较每个ArrayList之间的名词。由于我不关心动词,所以我从索引1开始搜索。(仅供参考)
一个可能的解决方案是使用数据库,如果有适当的索引,它可以很快完成搜索。假设数据适合内存,您可以更快。
原理
对于等问题
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));
现在,您迭代其他列表,只查看像这样的适当bucket中的元素
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
返回false的情况。
复杂性从二次型降低到线性,再加上对doSomething
的调用次数。
您的案例
您的数据结构显然不合适。你把你的三胞胎放在一个列表里,这是错误的。你当然可以以某种方式解决它,但创建一个class Triplet {String verb, noun1, noun2}
会让一切变得更简单。对于storeAsserions
,看起来您使用的是成对的。它们似乎有重叠,但这可能是一个拼写错误,不管怎样,这都无关紧要。让我们使用Triplet
s和Pair
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();
}
}
}
}
现在,我们需要在bucket上进行一些循环,这样至少有一个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));
明文:
对于来自CCD_ 13的每个CCD_v
为null时,则创建一个新列表v
y
添加到其中