【问题标题】:Algorithm for comparing two lists比较两个列表的算法
【发布时间】:2013-06-16 10:59:24
【问题描述】:

我有一个我在屏幕上显示的对象列表。用户可以随意更改它们,然后点击提交。在提交方法中,我将存储的原始列表和当前列表进行比较。我必须创建第三个列表,其中包含使用操作代码添加、删除或更改的所有对象,以指定它是哪一个。对象有一个 id 来识别它们,默认为 0。

我该怎么做?这是我能想到的最好的(伪代码),但看起来很草率。

for (currentObject : objects in current list)
    if (currentObject.id is 0)
        //Was added
    for (oldObject : objects in original list)
        if (currentObject.id == oldObject.id)
            //Existed - compare other fields to see if changed

for (oldObject1 : objects in original list)
    boolean existed = false;
    for (currentObject1 : objects in current list)
        if(oldObject1.id == currentObject1.id)
            existed=true;
    if (!existed)
        //Was removed

【问题讨论】:

  • 对象 ID 是否唯一?
  • 如果您的对象 ID 是唯一的,那么在比较它们之前对原始列表和当前列表进行排序可以获得很多好处。
  • @DavidH 对象 id 来自数据库是唯一的。但是在 UI 上添加的所有对象的 id 都是 0。

标签: java algorithm list data-structures


【解决方案1】:

如果排序无关紧要并且您只关心元素是添加还是删除,您可能需要考虑更改数据结构并使用Set 而不是ListSet 类型专门用于确定元素是否存在并有效地执行此操作,但代价是您不再记住元素的顺序。

例如,使用HashSet,您可以执行以下操作:

Set<T> oldElems = new HashSet<T>(originalList);
Set<T> newElems = new HashSet<T>(currentList);

for (T obj : oldElems) {
    if (!newElems.contains(obj)) {
        /* ... this object was removed ... */
    }
}

for (T obj : newElems) {
    if (!oldElems.contains(obj)) {
        /* ... this object was added ... */
    }
}

希望这会有所帮助!

【讨论】:

  • 另外,如果我没记错的话,Java 有一种方法可以识别两个集合共有的元素。
  • @Daniel- 有retainAllremoveAll 之类的操作,但它们破坏性地修改了原始容器。虽然也许这就是 OP 想要的?
  • 对于要在HashSet 中使用的类型,您需要确保正确实现equals()hashCode()。而且我看到你只是在处理添加和删除,那么修改后的情况呢?
  • @CostiCiudatu- 您可以将修改视为添加后删除(反之亦然)。在这里不行吗?
  • @Daniel 我尝试使用全部保留和全部删除,但没有成功。例如,如果我使用 oldList 和 removeAll(newList) 来获取所有已删除的元素,它会将修改视为删除,但事实并非如此。
【解决方案2】:

您可以通过一个循环来完成,方法是不断维护 3 路结果列表。如果您可以覆盖equals() 以依赖ids,那很好。如果没有,请检查 update 下的代码。

这是代码,请看下面的解释:

List<T> origList = ...;
List<T> newList = ...;

List<T> addedList = new ArrayList<T>();
List<T> deletedList = new ArrayList<T>();
List<T> changedList = new ArrayList<T>();

deletedList.addAll(origList);

for(T t : newList) {
    int origIndex = deletedList.indexOf(t);
    if (origIndex < 0) {
        addedList.add(t);
    } else {
        T origT = deletedList.remove(origIndex);
        if(t.compareTo(origT) != 0) {
            changedList.add(t);
        }
    }
}

请注意,我假设equals() 将检查id,而compareTo() 将检查所有其他字段。

说明:

您从deletedList 中删除了newList 中也存在的所有元素,因此结果是已删除 项。

您将所有未出现在原始列表中的新元素添加到 addedList

如果两者都存在并且对象不同,它们将转到changedList

如果两者都存在并且对象相同那么我们就不会在任何地方添加它。

注意事项:

如果新对象的 ID 为 0,它们将不会出现在 origList 中(因为我们假设它们已经被创建)。

当我上次实现这个时,我创建了一个单独的方法来逐个字段比较对象,所以我可以将比较逻辑与标准 Java 方法分开(实际上它也在interface)

我使用Lists 创建了它,但实际上您可以将它与任何类型的Collection 一起使用。与 List 一起使用将保留原始顺序。

更新:

尝试以这种方式覆盖您的equals(我跳过了类型检查和强制转换):

public boolean equals(T other) {
    if (this.id == 0) {
        return this == other;
    }
    return this.id == other.id;
}

尚未创建的实例仅与它们自己相等。已经创建的通过 id 检查。

【讨论】:

  • 我遇到的覆盖等于只检查 id 的问题是,在 UI 上,当他们单击删除时,我的动作侦听器正在调用 list.remove(Object o)。通过像这样覆盖 equals 会导致错误,因为 UI 列表中所有新添加的项目的 id=0。因此,当我删除它时,它只是删除了它找到的第一个 id=0 对象,而不一定是他们选择删除的对象。顺便说一句,这是一个 JSF 应用程序。
  • 如果您不能使用equals(),那么您可以将remove() 替换为Iterator 循环。我会更新我的答案。
  • 听起来不错,我去看看。查看您的代码,我认为您的意思是 addedList.add(t) 而不是 newList.add(t) 对吗?那部分让我有点失望。
  • @bhouse 是的,我的错。现在更正并添加了remove() 的代码。
  • 您提供的等号覆盖很好,可以解决我的其他问题。回到你原来的建议,T origT = deletedList.remove(t) 行这是一个语法错误,因为 remove(Obj) 返回一个布尔值。根据您尝试执行的操作,我认为我可以使用布尔值,将下一个 if 更改为 if(!booleanReturnedAbove) 但我对下一行的工作方式感到困惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-29
  • 1970-01-01
  • 1970-01-01
  • 2011-06-19
  • 2010-09-06
相关资源
最近更新 更多