简体   繁体   English

Stream 从两个列表直接到 SortedMap

[英]Stream from two lists directly to SortedMap

I am trying to generate a SortedMap using streams directly from two ArrayList s that come from a two different sources.我正在尝试使用来自两个不同来源的两个ArrayList的流直接生成一个SortedMap My goal is to have the SortedMap store a Double attribute from the second list as key and the object from the first list as the value, if those objects have a matching attribute, which is checked by a helper Object.我的目标是让SortedMap将第二个列表中的Double属性存储为键,并将第一个列表中的 object 作为值,如果这些对象具有匹配的属性,则由助手 Object 检查。

So far, I can get it accomplished using:到目前为止,我可以使用以下方法完成它:

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());

I have no use for myList that I am generating with that code, but so far it's the only way I have been able to populate the Map.我对使用该代码生成的myList没有用处,但到目前为止,这是我能够填充 Map 的唯一方法。 Is there a way I can populate directly to the SortedMap without having to generate that list?有没有一种方法可以直接填充到 SortedMap 而无需生成该列表?

Instead of creating a filter that causes side-effects you need to use collect .而不是创建一个会导致副作用的filter ,您需要使用collect

One of the ways to do that is to create an intermediate Map which would associate each object returned by firstDao with the matching pair returned by secondDao .一种方法是创建一个中间 Map ,它将 firstDao 返回的每个firstDaosecondDao返回的匹配对相关联。

Then create a stream over the entries of the intermediate map.然后在中间 map 的条目上创建 stream。 Filter out the entry with an empty key (with no matching pair).过滤掉带有空键(没有匹配对)的条目。 And then apply collect(Collectors.toMap()) .然后应用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
    ));

Another slightly more concise way of addressing this problem would be to create a custom collector by utilizing Collector.of() ( the overall logic remains the same ):解决此问题的另一种更简洁的方法是使用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
    ));

Sidenote: when you need a TreeMap it's better to use NavigableMap as an abstract type.旁注:当您需要TreeMap时,最好使用NavigableMap作为抽象类型。 This interface extends SortedMap and offers a wide range of methods which are not accessible with SortedMap .该接口扩展了SortedMap并提供了一系列SortedMap无法访问的方法。


Addressing the comment posted by OP解决OP发表的评论

This code provided in the question creates a stream of objects retrieved by the means of the firstDao and the following filter will update the map myMap if the predicate is meat by adding a new entry ( or replacing the value of the existing one ) with efficiency of the very first encountered matching object from the stream created using secondDao.get() as a source.问题中提供的此代码创建了通过 firstDao 检索的对象的firstDao ,如果谓词是肉类,则以下filter将更新 map myMap通过添加新条目(或替换现有条目的值), efficiency为第一个遇到的匹配 object 来自使用secondDao.get()作为源创建的 stream。

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

anyMatch - is a short-circuit operation and in case if there are the same id but different efficiency , they will be ignored . anyMatch - 是一种短路操作,如果id相同但efficiency不同,它们将被忽略

This snippet from the solution above behaves in exactly the same way: findFirst will pick the very first object for which someCondition will be evaluated to true (the case with an empty optional is being treated separately afterwards, and equals/hashCode of the optional is delegates to equals/hashCode implementation of its value, so there's no room for discrepancies in the result).上述解决方案中的这段代码的行为方式完全相同: findFirst将选择第一个object,其中someCondition将被评估为true (之后将单独处理空选项的情况,并且选项的equals/hashCode是委托到equals/hashCode实现其值,因此结果中没有差异的余地)。

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

And I assume it's the expected behavior because there is no mention that code provided in the question is malfunctioning in some ways, as well as there's no sample data (which might lead to the opposite conclusion).我认为这是预期的行为,因为没有提到问题中提供的代码在某些方面出现故障,并且没有示例数据(这可能会导致相反的结论)。

Hence, the claim that the above solution doesn't produce the expected result contradicts the current state of the question, since it's based on the logic of the original code.因此,上述解决方案不会产生预期结果的说法与问题的当前 state 相矛盾,因为它基于原始代码的逻辑。

Until the desired result isn't specified (by providing a sample data, or describing what's wrong with the original code) I see no possibility to improve the answer.未指定所需结果之前(通过提供示例数据或描述原始代码有什么问题),我认为不可能改进答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM