我有两个列表,其中包含每个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());
}
}
}
就我对初级开发人员的谦虚知识而言,我认为这样做是非常糟糕的做法?我应该实现一个比较器并使用它来迭代我的对象列表吗?我还应该检查空案例吗?
感谢大家的回答!
答案 0 :(得分:3)
假设Java 8并考虑到feedbackStatus可能包含多个具有相同ID的元素。
代码如下:
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;可读的。
将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();
}
}