如何通过比较 Java 中给定 objects 中的字段值来合并 objects 的两个列表

How to merge two lists of objects by comparing field value in the given objects in Java

我有两个 object 列表,我想将它们合并为一个。 object 有两个字段,"name" 和 "value"。对于 list2 中的给定 obj2,如果我们在 list1 中找到 obj1 的 "name" 字段的匹配项(obj1 来自 list1,obj2 来自 list2),那么我们使用 obj2 的 "value" 来覆盖 obj1。如果没有找到匹配项,那么我们将 obj2 添加到 list1。最终输出将更新为 list1.

有什么快速的方法吗?我能想到的就是用两个for循环来比较两个列表

中的所有object
class NameValueObj{
String name;
String value;
}

List<NameValueObj> merge(List<NameValueObj> list1, List<NameValueObj> list2){
// I want to merge two list here
}

NameValueObj 是给定的,所以我不能修改 object 源。

这是我的做法。

private List<Header> mergeHeaders(List<Header> defHeaders, List<Header> ovrdHeaders) {
        List<Header> lFinal = defHeaders;
        boolean foundMatch = false;
        for (Header ovrdHeader : ovrdHeaders) {
            foundMatch = false;
            for (Header defHeader : defHeaders) {
                if (defHeader.getName().equalsIgnoreCase(ovrdHeader.getName())) {
                    defHeader.setValue(ovrdHeader.getValue());
                    foundMatch = true;
                    break;
                }
            }
            if(!foundMatch) {
                lFinal.add(ovrdHeader);
            }

        }

        return lFinal;
    }

Header 有名称和值字段。 Headers 在给定列表中具有唯一名称。

因此,如果您看到主要的效率开销,您的方法是在另一个列表中搜索特定元素。所以你应该问如何才能有效地搜索另一个列表中的对象?

  • 数组(如果你知道索引)
  • HashMap(如果你知道密钥)

HashMap 看起来是解决问题的简单方法。所以你可以做的是遍历你的第一个列表并在地图中添加名称作为键和值作为值。第二个列表也是如此。

接下来遍历第一个映射的键集并在第二个列表中搜索相应的名称键。如果找到,请在第一个列表中添加为值。

这里的主要缺点是使用了额外的数据结构。

您的算法是 O(n*n)(二次)。

您可以在 O(n)(线性)中使用临时 LinkedHashMap

private List<Header> mergeHeaders(final List<Header> defHeaders, final List<Header> ovrdHeaders) {
    final Map<String, Header> headersMap = new LinkedHashMap<String, Header>();

    for (final Header defHeader : defHeaders) {
        headersMap.put(defHeader.getName().toLowerCase(), defHeader);
    }

    for (final Header ovrdHeader : ovrdHeaders) {
        headersMap.put(ovrdHeader.getName().toLowerCase(), ovrdHeader);
    }

    return new ArrayList<Header>(headersMap.values());
}

请注意,此行为与您的实施行为并不完全相同。 差异是:

  • 此实现 returns 一个新的列表实例,而不是修改第一个列表。 IMO,这是一个好处,但这可能取决于。如果需要,您可以按如下方式修改第一个列表(尽管我 推荐这样做):

    defHeaders.clear();
    defHeaders.addAll(headersMap.values());
    return defHeaders;
    
  • 此实现假设 header 名称在 both 列表中已经是唯一的(不区分大小写),而您的列表并未做出此假设.
    如果 header 名称 不是 唯一,此实现将保留列表 1 或列表 2 中的 最后一个 header。

我不得不解决类似的问题。在我的例子中,无论哈希映射是否已经有一个项目,我都必须创建自定义决策规则。这是我的最终解决方案:

辅助类:

public interface MergeRules<T> {
    T olderListContains(T olderObj, T newObj);

    T olderListNotContains(T newObj);
}

public abstract class HashAbstract {
    public String getKey() {
       return null;
    }
}

我的最终合并方法:

public static <T> List<T> merge(final List<T> olderList,
                                final List<T> newList,
                                MergeRules<T> mergeRules) {
    final Map<String, T> listMap = new LinkedHashMap<>();

    for (final T olderObj : olderList) {
        listMap.put(((HashAbstract) olderObj).getKey(), olderObj);
    }

    for (final T newObj : newList) {
        String newObjKey = ((HashAbstract) newObj).getKey();
        T returnObj;

        if (listMap.containsKey(newObjKey)) {
            returnObj = mergeRules.olderListContains(listMap.get(newObjKey), newObj);
        } else {
            returnObj = mergeRules.olderListNotContains(newObj);
        }

        listMap.put(newObjKey, returnObj);
    }

    return new ArrayList<>(listMap.values());
}

示例:

class NameValueObj extends HashAbstract {
    public String name;
    public String value;

    @Override
    public String getKey() {
        return name.trim().toUpperCase();
    }
}

NameValueObj obj1 = new NameValueObj();
obj1.name = "test";
obj1.value = "1234";

NameValueObj obj2 = new NameValueObj();
obj2.name = "testSomething";
obj2.value = "4321";

NameValueObj obj3 = new NameValueObj(); // obj that will be updated
obj3.name = "test";
obj3.value = "1212";

NameValueObj obj4 = new NameValueObj(); // new object
obj4.name = "testObj";
obj4.value = "1313";

List<NameValueObj> list1 = new ArrayList<>();
list1.add(obj1);
list2.add(obj2);

List<NameValueObj> list2 = new ArrayList<>();
list2.add(obj3);
list2.add(obj4);

List<NameValueObj> mergedList = merge(list1, list2, 
               new Compare.MergeRules<NameValueObj>() {
                    @Override
                    public NameValueObj olderListContains(NameValueObj olderObj, NameValueObj newObj) {
                        return newObj; // if obj already exists, return the new
                    }

                    @Override
                    public NameValueObj olderListNotContains(NameValueObj newObj) {                       
                        return newObj; // if not exists, return the new obj
                    }
                });

例子的mergedList是:

name: test - value: 1212
name: testSomething - value: 4321
name: testObj - value: 1313