繁体   English   中英

Java Reactive stream how to map an object when the object being mapped is also needed on the next step of the stream

[英]Java Reactive stream how to map an object when the object being mapped is also needed on the next step of the stream

我正在使用 Java 11 和项目 Reactor(来自 Spring)。 我需要对 rest api 进行 http 调用(我只能在整个流程中调用一次)。 有了响应,我需要计算两件事:

  1. 检查数据库(mongodb)中是否存在文档。 如果它不存在,则创建它并返回它。 否则就退货。
  2. 计算一些关于响应的逻辑,我们就完成了。

在伪代码中是这样的:

public void computeData(String id) {
    httpClient.getData(id) // Returns a Mono<Data>
        .flatMap(data -> getDocument(data.getDocumenId()))
         // Issue here is we need access to the data object consumed in the previous flatMap but at the same time we also need the document object we get from the previous flatMap
        .flatMap(document -> calculateValue(document, data)) 
        .subscribe();
}

public Mono<Document> getDocument(String id) {
    // Check if document exists
    // If not create document

    return document;
}

public Mono<Value> calculateValue(Document doc, Data data) {
    // Do something...
    return value;
}

问题是,calculateValue 需要 http.getData 的返回值,但这已经在第一个 flatMap 上使用了,但我们还需要我们从之前的 flatMap 获得的文档 object。

我尝试使用Mono.zip解决此问题,如下所示:

public void computeData(String id) {
    final Mono<Data> dataMono = httpClient.getData(id);

    Mono.zip(
        new Mono<Mono<Document>>() {
            @Override
            public void subscribe(CoreSubscriber<? super Mono<Document>> actual) {
                final Mono<Document> documentMono = dataMono.flatMap(data -> getDocument(data.getDocumentId()))
                actual.onNext(documentMono);
            }
        },
        new Mono<Mono<Value>>() {
            @Override
            public void subscribe(CoreSubscriber<? super Mono<Value>> actual) {
                actual.onNext(dataMono);
            }
        }
    )
    .flatMap(objects -> {
        final Mono<Document> documentMono = objects.getT1();
        final Mono<Data> dataMono = objects.getT2();

        return Mono.zip(documentMono, dataMono, (document, data) -> calculateValue(document, data))
    })
}

但这是执行httpClient.getData(id)两次,这违背了我只调用一次的约束。 我明白为什么要执行两次(我订阅了两次)。

也许我的解决方案设计可以在某个地方改进,但我看不到在哪里。 对我来说,这在设计响应式代码时听起来像是一个“正常”问题,但到目前为止我找不到合适的解决方案。

我的问题是,如何以反应性和非阻塞方式完成此流程,并且只调用一次 rest api?

附言; 我可以在一个 map 中添加所有逻辑,但这将迫使我订阅 map 内的 Mono 之一,这是不推荐的,我想避免采用这种方法。

关于@caco3 评论的编辑我需要在 map 中订阅,因为getDocumentcalculateValue方法都返回Mono

所以,如果我想把所有的逻辑放在一个单一的 map 中,它会是这样的:

public void computeData(String id) {
    httpClient.getData(id)
        .map(data -> getDocument(data).subscribe(s -> calculateValue(s, data)))
        .subscribe();
}

您不必在map内订阅,只需继续在flatMap内构建反应链:

getData(id) // Mono<Data>
        .flatMap(data -> getDocument(data.getDocumentId()) // Mono<Document>
                .switchIfEmpty(createDocument(data.getDocumentId())) // Mono<Document>
                .flatMap(document -> calculateValue(document, data)) // Mono<Value>
         )
        .subscribe()

归结起来,您的问题类似于:

Mono.just(1)
        .flatMap(original -> process(original))
        .flatMap(processed -> I need access to the original value and the processed value!
                System.out.println(original); //Won't work
        );


private static Mono<String> process(int in) {
    return Mono.just(in + " is an integer").delayElement(Duration.ofSeconds(2));
}

(愚蠢的例子,我知道。)

问题是map() (以及扩展的flatMap() )是转换- 您可以访问值,而旧值消失。 因此,在您的第二个flatMap()调用中,您可以访问1 is an integer ,但不是原始值( 1 。)

这里的解决方案是将 map 映射到包含原始值和新值的某种合并结果,而不是映射到新值。 Reactor 为此提供了一个内置类型 - Tuple 因此,编辑我们的原始示例,我们将拥有:

Mono.just(1)
        .flatMap(original -> operation(original))
        .flatMap(processed -> //Help - I need access to the original value and the processed value!
                System.out.println(processed.getT1()); //Original
                System.out.println(processed.getT2()); //Processed

                ///etc.
        );


private static Mono<Tuple2<Integer, String>> operation(int in) {
    return Mono.just(in + " is an integer").delayElement(Duration.ofSeconds(2))
            .map(newValue -> Tuples.of(in, newValue));
}

您可以使用相同的策略来“保留” documentdata - 无需内部订阅或任何类似的东西:-)

如何转换列表<object>至 Map<k, v> 使用 java stream<div id="text_translate"><p> 我想将List&lt;ObjectInList&gt;转换为Map&lt;K, V&gt;</p><pre> class ObjectInList { List&lt;Long&gt; listWithLong; Map&lt;String, Object&gt; dataMap; // there is 'id' key, and i want to use this id as key in map to be converted }</pre><p> 新的 map 格式如下</p><pre>String type; // this type is value of dataMap. List&lt;Long&gt; contents</pre><p> List&lt;Object&gt;中的每个 Object 都可以有重复的类型</p><p>例如</p><pre>///////// before converted //////////// [ { list: [1,2,3], dataMap: { type: "a", } }, { list: [4,5,6], dataMap: { type: "b", } }, { list: [7,8], dataMap: { type: "a", } }, ] ///////////// after converted ////////// { "a": [1,2,3,7,8], "b": [4,5,6] }</pre></div></k,></object>

[英]How to convert LIst<Object> to Map<K, V> with using java stream

暂无
暂无

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

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