我有两个清单。它们包含不同类型的对象,但这两种类型都包含id和name,而id是我要比较的。 列表一从DB中获取,列表二从前端发送。
我需要做的是遍历它们,找到新添加的列表项和删除的列表项。
我能够做到,但问题是它看起来很难看。
让我们说我有一个名为NameDTO的对象,它可以有id和name。列表2中填充了该类型的对象。
我就这样做了:
final ArrayList<NamedDTO> added = new ArrayList<>();
final ArrayList<NamedDTO> removed = new ArrayList<>();
for(NamedDTO listTwoObject : listTwo) {
boolean contained = false;
for(SomeObject listOneObject : listOne) {
if(listTwoObject.getId().equals(listOneObject.getId()) {
contained = true;
}
}
if(!contained) {
added.add(listTwoObject);
}
}
for(SomeObject listOneObject : listOne) {
boolean contained = false;
for(NamedDTO listTwoObject : listTwo) {
if(listTwoObject.getId().equals(listOneObject.getId()) {
contained = true;
}
}
if(!contained) {
removed.add(new NamedDTO(listOneObject.getId(), listOneObject.getName()));
}
}
这是有效的,我已经测试过了。 有更好的解决方案吗? 我正在考虑使用套装,所以我可以比较它们,这有什么缺点吗?
答案 0 :(得分:12)
如果我理解正确,这是示例场景:
[A, B, C, D]
[B, C, D, E, F]
以及您需要获得的效果是:
[E, F]
[A]
首先,我会使用某种类型的适配器或从一个公共类和override
equals
方法扩展不同的类型,以便您可以通过id
和{{1 }}
其次,这是非常简单的集合操作(你可以使用set&#39; s但列表也很好)。我建议使用库:https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html
现在基本上:
name
listTwo - listOne
并使用java代码:
listOne - listTwo
CollectionUtils.removeAll(listTwo, listOne)
否则,实施CollectionUtils.removeAll(listOne, listTwo)
(Java Docs)的所有馆藏也都有Collection
方法,您可以使用。
答案 1 :(得分:6)
这种嵌套列表处理不仅难看,而且效率低下。最好将一个列表的ID存储到$( "#select2element" ).select2({
placeholder: '---placeholder---',
allowClear: true,
minimumResultsForSearch: 5,
width:'100%'
});
中,以便进行有效查找,然后使用Set
处理另一个列表。这样,您就不会执行Set
次list1.size()
次操作,而是执行list2.size()
次list1.size()
次操作,这对于较大的列表来说是一个显着的差异。然后,由于两个操作基本相同,所以值得将它们抽象为一个方法:
list2.size()
此方法可用作
public static <A,B,R,ID> List<R> extract(
List<A> l1, List<B> l2, Function<A,ID> aID, Function<B,ID> bID, Function<A,R> r) {
Set<ID> b=l2.stream().map(bID).collect(Collectors.toSet());
return l1.stream().filter(a -> !b.contains(aID.apply(a)))
.map(r).collect(Collectors.toList());
}
由于交换两个列表需要辅助方法独立于元素类型,因此它需要用于访问id属性的函数,可以通过方法引用指定。然后,需要一个描述结果元素的函数,它是一种情况下的一个身份函数(只是得到List<NamedDTO> added = extract(listTwo, listOne, NamedDTO::getId, SomeObject::getId,
Function.identity());
List<NamedDTO> removed = extract(listOne, listTwo, SomeObject::getId, NamedDTO::getId,
so -> new NamedDTO(so.getId(), so.getName()));
)和另一种情况下从NamedDTO
构建NamedDTO
的lambda表达式。
操作本身如上所述一样直接,迭代一个列表,映射到id并收集到SomeObject
,然后迭代到另一个列表,只保留id不在的列表集合,映射到结果类型并收集到Set
。
答案 2 :(得分:6)
我建议使用java 8流解决方案:
ArrayList<ObjOne> list = new ArrayList<>(Arrays.asList(new ObjOne("1","1"),new ObjOne("3","3"),new ObjOne("2","2")));
ArrayList<ObjTwo> list2 = new ArrayList<>(Arrays.asList(new ObjTwo("1","1"),new ObjTwo("3","3"),new ObjTwo("4","4")));
List<ObjOne> removed = list.stream().filter(o1 -> list2.stream().noneMatch(o2 -> o2.getId().equals(o1.getId())))
.collect(Collectors.toList());
System.out.print("added ");
removed.forEach(System.out::println);
List<ObjTwo> added = list2.stream().filter(o1 -> list.stream().noneMatch(o2 -> o2.getId().equals(o1.getId())))
.collect(Collectors.toList());
System.out.print("removed ");
added.forEach(System.out::println);
这基本上是您的解决方案,但使用流实现,这将使您的代码更短,更易于阅读
答案 3 :(得分:2)
如果这些id是唯一的,你可以把它们放在一个HashSet中,找到你感兴趣的id:
Set<Integer> uiList = Stream.of(new FromUI(1, "db-one"), new FromUI(2, "db-two"), new FromUI(3, "db-three"))
.map(FromUI::getId)
.collect(Collectors.toCollection(HashSet::new));
Set<Integer> dbList = Stream.of(new FromDB(3, "ui-one"), new FromDB(5, "ui-five"))
.map(FromDB::getId)
.collect(Collectors.toCollection(HashSet::new));
uiList.removeIf(dbList::remove);
added/uiSet : [1,2]
removed/dbSet : [5]
我创建了FromUI
和FromDB
类,其构造函数将id和name作为输入。
我还假设如果一个元素包含在uiSet
中,而不是dbSet
中,则添加了另一种方式。