迭代和比较大数据集

时间:2016-10-21 06:30:31

标签: java

基本上我从2个不同的数据库收到2个大数据列表,列表如下:

List 1:
=============
A000001
A000002
A000003
.
.
A999999

List 2:
=============
121111
000111
000003
000001
.
.

我需要比较两个列表,并找出List 1 List 2中可用的每个数据(在添加一些标准密钥后),以便如果可用,请将其放入第3个列表以进行进一步操作。例如,A000001以及List 1 (在添加一些标准密钥后)中可以使用List 2,所以我需要将其放在第3个列表中

基本上我有这个代码,它对List 1中的每一行都这样,我正在迭代List 2中的所有数据并进行比较。 (两者都是数组列表)

List<String> list1 = //Data of list 1 from db
List<String> list2 = //Data of list 2 from db

for(String list1Item:list1) {
   for(String list2Item:list2) {
     String list2ItemAfterAppend = "A" + list2Item;
     if(list1Item.equalsIgnoreCase(list2ItemAfterAppend)) {
        //Add it to 3rd list
     }
   }
}

是的,这个逻辑工作正常,但我觉得这不是迭代列表的有效方法。放置定时器后,2000x5000数据列表平均需要13444毫秒。我的问题是,您是否有其他逻辑可供人们思考或建议我改进此代码的性能?

我希望我很清楚,如果不是,请告诉我是否可以提出问题。

5 个答案:

答案 0 :(得分:1)

您可以对两个列表进行排序,然后在两个值上仅使用一个循环迭代,根据哪个值最大来切换哪个索引递增。类似的东西:

boolean isWorking = true;
Collections.sort(list1);
Collections.sort(list2);
int index1 = 0;
int index2 = 0;

while(isWorking){
    String val1 = list1.get(index1);
    String val2 = "A" + list2.get(index2);
    int compare = val1.compareTo(val2)

    if(compare == 0){
        list3.add(val1);
        index1++;
        index2++;
    }else if (compare > 0){
        val2++;
    }else{ // if(compare < 0)
        val1++;
    }

    isWorking = !(index1 == list1.size() || index2 == list2.size() );
}

小心你正在使用什么类型的List。 get(int i)上的LinkedList价格昂贵,而ArrayList上则list1.size()。此外,您可能希望保存list2.size()val1++,我不认为它每次都会对其进行计算,但是请将其解决。我不确定它是否真的有用/高效,但是你可以用两个列表中最小的列表初始化list3(考虑到loadFactor,查找它),所以list3不必每次调整大小。

上面的代码未经过测试(可能会切换val2++sort()),但您明白了。我相信它比你的更快(因为它是O(n + m)而不是O(n * m)但是我会让你看到({1}}和compareTo()都会增加一些时间方法,但通常它不应该太多。)如果可以的话,使用你的RDBMS在你得到它们时对两个列表进行排序(所以你不必在Java代码中这样做)

答案 1 :(得分:0)

我认为问题在于列表有多大以及你有多少内存。 对于我不到100万条记录,我将使用HashSet来加快速度。 代码可能会像:

Set<String> set1 = //Data of list 1 from db, when you get the data you make it a Set instead of a List. HashSet is enough for you to use.
List<String> list2 = //Data of list 2 from db

然后你只需要:

for(String list2Item:list2) {
    if(set1.contains("A" + list2Item) {
    }
}

希望这可以帮到你。

答案 2 :(得分:0)

您可以使用apache commons中的intersection方法。例如:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.collections4.CollectionUtils;

public class NewClass {

    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("A000001","A000002","A000003");
        List<String> list2 = Arrays.asList("121111","000111","000001");
        List<String> list3 = new ArrayList<>();
        list2.stream().forEach((s) -> {list3.add("A"+s);});
        Collection<String> common =  CollectionUtils.intersection(list1, list3);       
    }
}

答案 3 :(得分:0)

您可以尝试使用Stream API,使用Streams创建新列表的代码非常简洁明了,性能可能非常相似:

    List<String> list3 = list2.stream()
                              .map(s->"A"+s)
                              .filter(list1::contains)
                              .collect(Collectors.toList());

如果列表很大,您可以尝试并行处理列表并使用多个线程来处理列表。 可能会也可能不会改善效果。做一些测量非常重要,以检查并行处理列表是否实际上提高了性能。

要并行处理流,您只需要在流上调用方法parallel

    List<String> list3 = list2.stream()
                              .parallel()
                              .map(s->"A"+s)
                              .filter(list1::contains)
                              .collect(Collectors.toList()); 

答案 4 :(得分:0)

你的代码正在进行大量的字符串操作,'equalsIgnoreCase'将字符转换为大写/小写。这是在你的内循环中执行的,列表的大小是5000x2000,因此String操作正在进行数百万次。

理想情况下,从数据库中获取大写或小写的字符串,并避免内部循环内的转换。如果这是不可能的,那么可能在开头转换字符串的情况会改善性能。

然后,您可以使用其中一个列表的元素创建一个新列表,并将所有元素保留在另一个列表中,具有大写转换的代码可以是:

list1.replaceAll(String::toUpperCase);
List<String> list3 = new ArrayList<>(list2);
list3.replaceAll(s->"A"+s.toUpperCase());
list3.retainAll(list1);