根据两个列表的内容生成排序映射



我正在尝试使用直接来自两个不同来源的两个ArrayList的流生成SortedMap。我的目标是让SortedMap将第二个列表中的Double属性存储为键,并将第一个列表中的对象存储为值,前提是这些对象具有匹配的属性,该属性由帮助程序对象检查。

到目前为止,我可以使用以下方法完成它:

SortedMap<Double, FirstObject> myMap = new TreeMap<>(Double::compareTo);
List<FirstObject> myList = firstDao.get(someId).stream()
.filter(firstobject -> secondDao.get(firstObject.getObjectId())
.stream()
.anyMatch(secondObject -> {
if (Helper.check(secondObject).matches()) {
myMap.put(
secondObject.getEfficiency(), firstObject
);
}
return Helper.check(secondObject).matches();
}))
.collect(Collectors.toList());

我对使用该代码生成的myList没有任何用处,但到目前为止,这是我能够填充Map的唯一方法。

有没有办法直接填充到SortedMap而无需生成该列表?

而不是创建一个导致副作用的filter,你需要使用collect

执行此操作的方法之一是创建一个中间 Map,它将firstDao返回的每个对象与secondDao返回的匹配对相关联。

然后在中间映射的条目上创建一个流。筛选出具有空键(没有匹配对)的条目。然后应用collect(Collectors.toMap()).

NavigableMap<Double, FirstObject> myMap = firstDao.get(tableId).stream()
.collect(Collectors.toMap( // creates a map `Map<Optional<Double>,FirstObject>`
firstObject -> secondDao.get(firstObject.getObjectId()).stream()
.filter(secondObject -> firstObject.getAttribute().equals(secondObject.getAttribute()))
.findFirst()
.map(FirstObject::getEfficiency),
Function.identity(),
(left, right) -> left // duplicated keys would appear when there are more then one object having no matching pair (the key would be an empty optional), so we need to guard against that case by providing a merge function to resulve duplicates
))
.entrySet().stream()
.filter(entry -> entry.getKey().isPresent())
.collect(Collectors.toMap(
entry -> entry.getKey().get(),    // extracting a key (efficiency)
Map.Entry::getValue,              // extracting a value
(left, right) -> { throw new AssertionError("duplicates are not expected"); }, // since the source is an intermediate map we don't expect duplicates
TreeMap::new
));

解决此问题的另一种稍微简洁的方法是利用Collector.of()创建自定义收集器(整体逻辑保持不变):

NavigableMap<Double, FirstObject> myMap = firstDao.get(tableId).stream()
.collect(Collector.of(
TreeMap::new,                                          // mutable container of the collector
(NavigableMap<Double, FirstObject> map, FirstObject firstObject) -> 
secondDao.get(firstObject.getObjectId()).stream()  // population the mutable container
.filter(secondObject -> next.getAttribute().equals(secondObject.getAttribute()))
.findFirst()
.map(FirstObject::getEfficiency)
.ifPresent(efficiency -> map.put(efficiency, firstObject)),
(left, right) -> { left.putAll(right); return left; } // merging containers in parallel, I'm assuming that there's no duplicates and hence there's no special policy
));

旁注:当您需要TreeMap时,最好将NavigableMap用作抽象类型。该接口扩展了SortedMap,并提供了SortedMap无法访问的各种方法。


处理OP发表的评论

问题中提供的此代码创建通过firstDao检索的对象流,以下filter将通过添加新条目(或替换现有条目的值)来更新映射,myMap谓词是否是肉,方法是使用secondDao.get()作为源创建的流中第一个遇到的匹配对象的efficiency

.filter(firstobject -> secondDao.get().stream()
.anyMatch(secondObject -> {
if (someCondition) {
myMap.put(secondObject.getEfficiency(), firstObject);
}
return someCondition;
}))

anyMatch- 是短路操作,如果存在相同idefficiency不同,它们将被忽略

上面解决方案中的这个代码片段的行为方式完全相同:findFirst将选择第一个对象,someCondition将被评估为true(之后将单独处理具有空可选功能的情况,并且equals/hashCode可选是委托equals/hashCode其值的实现, 所以结果没有差异的余地)。

firstObject -> secondDao.get().stream()
.filter(secondObject -> someCondition)
.findFirst()
.map(FirstObject::getEfficiency)

我认为这是预期的行为,因为没有提到问题中提供的代码在某些方面出现故障,也没有示例数据(这可能会导致相反的结论)。

因此,上述解决方案没有产生预期结果的说法与问题的当前状态相矛盾,因为它基于原始代码的逻辑。

在没有指定所需的结果之前(通过提供示例数据或描述原始代码的问题),我认为不可能改进答案。

最新更新