我正在尝试学习如何将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?
}
}
我在哪里迷路了!
感谢您的指导。
答案 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;
}