通过具有相同类型对象的两个不同List(Java8)有效地迭代

时间:2017-03-24 15:15:50

标签: java performance for-loop collections java-8

我有两个列表,其中包含每个N元素的重要对象数:

List<Foo> objectsFromDB = {{MailId=100, Status=""}, {{MailId=200, Status=""}, {MailId=300, Status=""} ... {MailId=N , Status= N}}

List <Foo> feedBackStatusFromCsvFiles = {{MailId=100, Status= "OPENED"}, {{MailId=200, Status="CLICKED"}, {MailId=300, Status="HARDBOUNCED"} ... {MailId=N , Status= N}} 

小见解:  objectFromDB通过调用Hibernate方法检索我的数据库行。

feedBackStatusFromCsvFiles调用CSVparser方法并解组到Java对象。

我的实体类Foo包含所有设置者和获取者。所以我知道基本的想法是使用这样的foreach:

     for (Foo fooDB : objectsFromDB) {
          for(Foo fooStatus: feedBackStatusFromCsvFiles){
              if(fooDB.getMailId().equals(fooStatus.getMailId())){
                    fooDB.setStatus(fooStatus.getStatus());
                }
               }
            }

就我对初级开发人员的谦虚知识而言,我认为这样做是非常糟糕的做法?我应该实现一个比较器并使用它来迭代我的对象列表吗?我还应该检查空案例吗?

感谢大家的回答!

4 个答案:

答案 0 :(得分:3)

假设Java 8并考虑到feedbackStatus可能包含多个具有相同ID的元素。

  1. 使用ID作为键并使用元素列表将列表转换为Map。
  2. 迭代列表并使用Map查找所有消息。
  3. 代码如下:

    final Map<String, List<Foo>> listMap = 
    objectsFromDB.stream().collect(
          Collectors.groupingBy(item -> item.getMailId())
    );
    
    for (final Foo feedBackStatus : feedBackStatusFromCsvFiles) {
            listMap.getOrDefault(feedBackStatus.getMailId(), Colleactions.emptyList()).forEach(item -> item.setStatus(feedBackStatus.getStatus()));
    }
    

答案 1 :(得分:2)

使用集合中的贴图来避免嵌套循环。

    List<Foo> aList = new ArrayList<>();
    List<Foo> bList = new ArrayList<>();
    for(int i = 0;i<5;i++){
        Foo foo = new Foo();
        foo.setId((long) i);
        foo.setValue("FooA"+String.valueOf(i));
        aList.add(foo);
        foo = new Foo();
        foo.setId((long) i);
        foo.setValue("FooB"+String.valueOf(i));
        bList.add(foo);
    }

    final Map<Long,Foo> bMap = bList.stream().collect(Collectors.toMap(Foo::getId, Function.identity()));

    aList.stream().forEach(it->{
        Foo bFoo = bMap.get(it.getId());
        if( bFoo != null){
            it.setValue(bFoo.getValue());
        }
    });

唯一的其他解决方案是让DTO图层返回MailId-&gt; Foo对象的地图,然后您可以使用CVS列表进行流式传输,只需查找DB Foo对象即可。否则,对两个列表进行排序或迭代的费用不值得在性能时间上进行权衡。之前的声明是正确的,直到它明确地导致平台上的内存约束,直到那时让垃圾收集器完成它的工作,并且尽可能简单地完成它。

答案 2 :(得分:1)

鉴于您的列表可能包含数万个元素,您应该担心简单的嵌套循环方法会太慢。它肯定会比它需要做更多的比较。

如果记忆相对丰富,那么最快的合适方法可能是从你的一个列表中形成一个从mailId到(列表)相应Foo的地图,有点像@MichaelH建议的那样,并使用它来匹配mailIds。但是,如果mailId值在一个或两个列表中不一定是唯一的,那么您需要的东西与迈克尔的特定方法略有不同。即使mailId确定在两个列表中都是唯一的,但只创建一个地图会更有效。

对于最常见的情况,您可能会这样做:

// The initial capacity is set (more than) large enough to avoid any rehashing
Map<Long, List<Foo>> dbMap = new HashMap<>(3 * objectFromDb.size() / 2);

// Populate the map
// This could be done more effciently if the objects were ordered by mailId,
// which perhaps the DB could be enlisted to ensure.
for (Foo foo : objectsFromDb) {
    Long mailId = foo.getMailId();
    List<Foo> foos = dbMap.get(mailId);

    if (foos == null) {
        foos = new ArrayList<>();
        dbMap.put(mailId, foos);
    }
    foos.add(foo);
}

// Use the map
for (Foo fooStatus: feedBackStatusFromCsvFiles) {
    List<Foo> dbFoos = dbMap.get(fooStatus.getMailId());

    if (dbFoos != null) {
        String status = fooStatus.getStatus();

        // Iterate over only the Foos that we already know have matching Ids
        for (Foo fooDB : dbFoos) {
            fooDB.setStatus(status);
        }
    }
}

另一方面,如果你是空间受限的,那么创建地图是不可行的,但是重新排序你的两个列表是可以接受的,那么你仍然可以通过先对两个列表进行排序来提高性能。为此,您可能会将Collections.sort()与适当的Comparator一起使用。然后,您将在每个列表上获得Iterator,并使用它们在两个列表上协作迭代。我没有提供任何代码,但它会让人联想到合并排序的合并步骤(但这两个列表实际上并未合并;您只能将状态信息从一个复制到另一个)。但是,只有来自mailId的{​​{1}}都是不同的,这才有意义,否则整个任务的预期结果就不能很好地确定。

答案 3 :(得分:1)

你的问题是将Foo的最后状态合并到数据库对象中。所以你可以通过两个步骤来实现它,这将使它更加清晰&amp;可读的。

  1. 过滤需要合并的Foos。
  2. 将Foos与上次状态合并。

    std::vector<int> myvector;
    
    ...
    
    void feefiefoefoo(int** matrix, int rows, int cols) {
        int vec_size = myvector.size();
        int column_pointer = 0;
        auto it = myvector.begin();
    
        for (int i = 0; i < rows; ++i) {
            for (int j = column_pointer; j < vec_size; ++j) {
                matrix[i][j] = *it;
                it = std::next(it); // **THIS IS THE LINE**
            }
    
            column_pointer++;
            vec_size++;
            it = myvector.begin();
        }
    }