我目前正在开发一个项目,我需要使用大约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处搜索。(仅针对某些情况)
答案 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
,看起来你正在使用对。它们似乎重叠,但这可能是一个错字,无论如何它并不重要。我们使用Triplet
和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();
}
}
}
}
现在,我们需要在桶上进行一些循环,因此至少有一个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