使用“ id”字段更新列表的小节

时间:2018-08-18 15:45:25

标签: java list lambda java-8 java-stream

我正在尝试学习如何将lambda函数用于更简洁的代码,但仍在努力使之工作。

我有两个列表。 “旧”列表总是短于或与“更新列表”相同。 我想从“更新的列表”中获取对象,并在较短的“旧列表”中覆盖“陈旧的对象”。 列表中的每个对象都有一个唯一的字段。

例如,这有点像使用新版本更新图书馆中的书籍。 UUID(标题+作者)保持不变,但是新对象将书架上的旧对象替换为新书/对象。

我知道我可以“很长一段路子”做一个HashMap<MyUniqueFieldInMyObject, MyObject>,然后拿起新的List<MyUpdatedObjects>来做。

即分别具有HashMap<UniqueField, MyOldObject>HashMap<UniqueField, MyUpdatedObject>,然后使用伪“在更新的对象具有相同键的条目时,用更新的值覆盖该值”来伪造旧对象。

但是...

使用功能性lambda语句是否有一种“更简单”的方法?

我在考虑以下方面:

List<MyObject> updatedList;
List<MyObject> oldList;

updatedList.forEach(MyObject -> {

    String id = MyObject.getId();

    if (oldList.stream().anyMatcher(MyObject -> 
        MyObject.getId().matches(id)) {
           //Do the replacement here? If so...how?
       }
}

我在哪里迷路了!

感谢您的指导。

4 个答案:

答案 0 :(得分:2)

如果您想就地更新列表而不是创建新列表,则可以使用List.replaceAll

oldList.replaceAll(old -> 
    updateListe.stream()
        .filter(updated -> updated.getId().equals(old.getId())
        .findFirst()
        .orElse(old)
);

此解决方案的主要问题是它的复杂度为O(旧大小*更新大小)。您所说的“漫长的路途”方法可以保护您,不必为旧列表中的每个条目遍历整个更新后的列表:

// note that this will throw if there are multiple entries with the same id
Map<String, MyObject> updatedMap = updatedList.stream()
                                         .collect(toMap(MyObject::getId, x->x));

oldList.replaceAll(old -> updatedMap.getOrDefault(old.getId(), old));

答案 1 :(得分:1)

我建议您遍历要更新的oldList。对于迭代的每个对象,将与其等效的对象与其id匹配,并使用Stream::map替换它。如果找不到对象,请使用Optional::orElse将其替换为self(不更改对象)。

List<MyObject> newList = oldList
    .stream()                                                  // Change values with map()
    .map(old -> updatedList.stream()                           // Iterate each to find...
            .filter(updated -> old.getId() == updated.getId()) // ...by the same id
            .findFirst()                                       // Get new one to replace
            .orElse(old))                                      // Else keep the old one
    .collect(Collectors.toList());                             // Back to List

答案 2 :(得分:1)

List<Foo> updatedList = List.of(new Foo(1L, "new name", "new desc."));
List<Foo> oldList = List.of(new Foo(1L, "old name", "old desc."));

List<Foo> collect = Stream.concat(updatedList.stream(), oldList.stream())
            .collect(collectingAndThen(toMap(Foo::getId, identity(), Foo::merge), 
                     map -> new ArrayList(map.values())));

System.out.println(collect);

这将打印出: [Foo{id=1, name='new name', details='old desc.'}]

Foo::merge中,您可以定义哪些字段需要更新:

class Foo {
    private Long id;
    private String name;
    private String details;

    /*All args constructor*/
    /*getters*/

    public static Foo merge(Foo newFoo, Foo oldFoo) {
        return new Foo(oldFoo.id, newFoo.name, oldFoo.details);
    }
}

答案 3 :(得分:0)

我认为最好将要更新的对象添加到新列表中,以避免更改正在流式传输的列表,然后只需将旧列表替换为新列表即可。

private List<MyObject> update(List<MyObject> updatedList, List<MyObject> oldList) {
    List<MyObject> newList = new ArrayList<>();

    updatedList.forEach(object -> {
        if (oldList.stream().anyMatch(old -> old.getUniqueId().equals(object.getUniqueId()))) {
            newList.add(object);
        }
    }

    return newList;
}