Java 比较两个列表并获得差异

Java 比较两个列表并获得差异,java,spring-boot,java-8,Java,Spring Boot,Java 8,我有两张单子。它们包含不同类型的对象,但这两种类型都包含id和name,id就是我要比较的对象。 列表1从数据库中获取,列表2从前端发送 我需要做的是循环浏览它们,找出哪些列表项是新添加的,哪些是删除的 我能做到,但问题是它看起来很丑 假设我有一个名为NameDTO的对象,它可以有id和name。列表二充满了这种类型的对象 我就是这样做的: final ArrayList<NamedDTO> added = new ArrayList<>(); final ArrayLi

我有两张单子。它们包含不同类型的对象,但这两种类型都包含id和name,id就是我要比较的对象。 列表1从数据库中获取,列表2从前端发送

我需要做的是循环浏览它们,找出哪些列表项是新添加的,哪些是删除的

我能做到,但问题是它看起来很丑

假设我有一个名为NameDTO的对象,它可以有id和name。列表二充满了这种类型的对象

我就是这样做的:

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()));
  }
}
final ArrayList added=new ArrayList();
最终删除的ArrayList=新的ArrayList();
for(namedTo listTwoObject:listTwo){
布尔值=假;
对于(SomeObject listOneObject:listOne){
如果(listTwoObject.getId().equals)(listOneObject.getId()){
包含=真;
}
}
如果(!包含){
add.add(listTwoObject);
}
}
对于(SomeObject listOneObject:listOne){
布尔值=假;
for(namedTo listTwoObject:listTwo){
如果(listTwoObject.getId().equals)(listOneObject.getId()){
包含=真;
}
}
如果(!包含){
添加(新名称添加到(listOneObject.getId(),listOneObject.getName());
}
}
这是有效的,我已经测试过了。 有更好的解决方案吗?
我在考虑使用集合,以便比较它们,这有什么缺点吗?

如果我理解正确,这是示例场景:

  • 列表一[datab]项:
    [A、B、C、D]
  • 列出两个[前面]项:
    [B,C,D,E,F]
你需要得到的效果是:

  • 新增:
    [E,F]
  • 删除:
    [A]

首先,我将使用一些类型适配器或从一个公共类扩展不同的类型,并覆盖
equals
方法,以便您可以通过
id
name
匹配它们

其次,这是对集合的非常简单的操作(您可以使用集合,但列表也可以)。我建议使用库:

现在基本上:

  • 添加的是
    listwo-listOne
  • 删除的是
    listOne-listwo
并使用java代码:

  • 添加:
    CollectionUtils.removeAll(列表二,列表一)
  • 删除:
    CollectionUtils.removeAll(列表一、列表二)


否则,所有实现
Collection
()的集合也有
removeAll
方法,您可以使用该方法。

如果这些id是唯一的,您可以将它们放在哈希集中,从而找到您感兴趣的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]
Set-uiList=Stream.of(新FromUI(1,“db-one”)、新FromUI(2,“db-two”)、新FromUI(3,“db-two”))
.map(FromUI::getId)
.collect(Collectors.toCollection(HashSet::new));
Set dbList=Stream.of(新FromDB(3,“ui一”),新FromDB(5,“ui五”))
.map(FromDB::getId)
.collect(Collectors.toCollection(HashSet::new));
removeIf(dbList::remove);
新增/uiSet:[1,2]
已删除/数据库集:[5]
我已经创建了
FromUI
FromDB
类,其中的构造函数将id和名称作为输入


我还假设,如果一个元素包含在
uiSet
中,但不包含在
dbSet
中,则添加了另一种方法。

这种嵌套列表处理不仅难看,而且效率低下。最好将一个列表的ID存储到
集中,以便进行有效的查找,然后处理其他列表使用
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());
}
公共静态列表提取(
列表l1、列表l2、功能辅助、功能投标、功能r){
Set b=l2.stream().map(bID.collect(Collectors.toSet());
返回l1.stream().filter(a->!b.contains(aID.apply(a)))
.map(r.collect(Collectors.toList());
}
此方法可作为

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()));
List added=extract(listTwo,listOne,NamedTo::getId,SomeObject::getId,
Function.identity());
List removed=extract(listOne、listwo、SomeObject::getId、NamedDTO::getId、,
so->newnameddto(so.getId(),so.getName());
由于交换两个列表需要helper方法独立于元素类型,因此它需要访问id属性的函数,该属性可以通过方法引用指定。然后,需要一个描述结果元素的函数,在一种情况下,该函数是一个标识函数(仅获取
namedTo
)以及一个lambda表达式,该表达式从另一个对象中的某个对象构建一个
namedTo


操作本身与上述一样简单,迭代一个列表,映射到id并收集到
集合
,然后迭代另一个列表,只保留id不在集合中的元素,映射到结果类型并收集到
列表

,我建议使用java 8 streams解决方案:

    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);
ArrayList list=新的ArrayList(Arrays.asList(新对象(“1”、“1”)、新对象(“3”、“3”)、新对象(“2”、“2”));
ArrayList list2=新的ArrayList(Arrays.asList(新的ObjTwo(“1”、“1”)、新的ObjTwo(“3”、“3”)、新的ObjTwo(“4”、“4”));
移除列表=List.stream().filter(o1->list2.stream().noneMatch(o2->o2.getId().equals(o1.getId()))
.collect(Collectors.toList());
系统输出打印(“添加”);
删除.forEach(System.out::println);
添加的List=list2.stream().filter(o1->List.stream().noneMatch(o2->o2.getId().equals(o1.getId()))
.收集(