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

时间:2015-02-11 23:10:19

标签: java arraylist merge

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

有没有快速的方法来做到这一点?我能想到的是使用两个for循环来比较两个列表中的所有对象

class NameValueObj{
String name;
String value;
}

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

NameValueObj是给定的,因此我可以修改对象源。

这是我的做法。

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;
    }

标题有名称和值字段。标题在给定列表中具有唯一的名称。

3 个答案:

答案 0 :(得分:5)

您的算法为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());
}

请注意,行为与您的实施行为不完全相同。 不同之处是:

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

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

答案 1 :(得分:0)

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

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

HashMap看起来像是一种实现问题的简单方法。所以你可以做的是遍历你的第一个列表并在地图中添加名称作为键和值作为值。同样的第二个清单。

接下来迭代第一个映射的键集,并在第二个列表中搜索相应的名称键。如果发现在第一个列表中添加为值。

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

答案 2 :(得分:0)

我不得不解决类似的问题。就我而言,我必须在Hash Map中已有项目时创建自定义决策规则。这是我的最终解决方案:

辅助类:

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