使用“id”字段更新列表的子部分
Posted
技术标签:
【中文标题】使用“id”字段更新列表的子部分【英文标题】:Updating a subsection of a list with an "id" field 【发布时间】:2019-01-25 08:56:46 【问题描述】:我正在尝试学习如何使用 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?
这是我迷路的地方!
感谢您的指导。
【问题讨论】:
如果旧列表中没有新书会怎样?要将其添加到旧列表中吗? 没有。我只想更新旧列表中当前的那些。 好的。如果这本书在旧列表中,它肯定在新列表中吗? 没有。但我将在单独的步骤中删除旧列表中不在新列表中的书籍。 IE。这就像一家书店供应商提供其整个书库的大型目录,但图书馆只对更新它当前拥有且可以更新的目录感兴趣。 【参考方案1】:如果您想就地更新列表而不是创建新列表,可以使用List.replaceAll
:
oldList.replaceAll(old ->
updateListe.stream()
.filter(updated -> updated.getId().equals(old.getId())
.findFirst()
.orElse(old)
);
这个解决方案的主要问题是它的复杂度是 O(size-of-old*size-of-updated)。您描述为“漫长”的方法可以保护您不必为旧列表中的每个条目遍历整个更新列表:
// 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));
【讨论】:
所以基本上,语法上更长的方式实际上是最多的,为了在移动应用程序上大约 1000-10,000 个条目,最好的选择是在亚赫兹更新到大约 10 毫秒?跨度> 【参考方案2】:我建议您迭代 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
【讨论】:
【参考方案3】: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);
这将打印出:
[Fooid=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);
【讨论】:
【参考方案4】:我认为最好将要更新的对象添加到新列表中以避免更改您正在流式传输的列表,然后您可以简单地将旧列表替换为新列表
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;
【讨论】:
以上是关于使用“id”字段更新列表的子部分的主要内容,如果未能解决你的问题,请参考以下文章
使用`brew info package`时`Dependencies`部分的子部分是啥意思?
Snakemake,RNA-seq:如何根据所分析样本的特征执行管道的一个子部分或另一个子部分?