簡體   English   中英

Java 8:Stream:消耗步驟以保存流元素的中間狀態

[英]Java 8 : Stream : Consuming steps to save Intermediate States of streamed elements

首先,對這個抽象標題感到抱歉。 舉個例子,這個想法更簡單。 假設我在列表L中有一些值。我想構建請求服務的參數,然后調用該服務並收集所有響應。

目前,我正在使用這種代碼結構:

private final List<String> bodies = ImmutableList.of(
        "1", "2", "3", "4"
);

@Test
public void BasicRequestResponseStreamToList() {

    final List<Request> requests = bodies.stream()
            .map(Request::new)
            .collect(Collectors.toList());

    final List<Response> responses = requests.stream()
            .map(service::send)
            .collect(Collectors.toList());

    commonAssertions(requests, responses);

}

但是,考慮到必須在發送第一個請求之前構建最后一個請求,我發現兩個流的效率不高。 我想做類似的事情:

@Test
public void StatefulMapperStreamRequestResponseToList() {

    final List<Request> requests = new ArrayList<>();
    final List<Response> responses = bodies.stream()
            .map(Request::new)
            .map(x -> {
                requests.add(x);
                return service.send(x);
            })
            .collect(Collectors.toList());

    commonAssertions(requests, responses);

}

但是,我對將這樣的“ Hack”用於映射語義感到內。 但是,這是我發現用延遲加載構建2個相關列表的唯一方法。 第一個解決方案對我不感興趣,因為它必須等待構建所有請求,然后再發送它們。 我很想在EIP中實現類似竊聽的功能。 http://camel.apache.org/wire-tap.html

與修改map方法的語義來實現這一目標相比,我很樂意讓您想到一種更優雅的方式。

如果有幫助,您可以在這里找到源: http : //tinyurl.com/hojkdzu

使用.peek()雖然需要在代碼中進行較少的更改,但實際上是一個很骯臟的解決方案。 您需要它,因為您的原始代碼中存在設計缺陷。 您具有“並行數據結構”(可能是術語不是很好): requests列表中的第一個元素對應於responses列表中的第一個元素,依此類推。 遇到這種情況時,請考慮創建一個新的PoJo類。 像這樣:

public class RequestAndResponse { // You may think up a better name
    public final Request req; // use getters if you don't like public final fields
    public final Response resp;

    public RequestAndResponse(Request req, Response resp) {
        this.req = req;
        this.resp = resp;
    }
}

現在,您的問題神奇地消失了。 你可以寫:

List<RequestAndResponse> reqresp = bodies.stream()
        .map(Request::new)
        .map(req -> new RequestAndResponse(req, service.send(req)))
        .collect(Collectors.toList());

commonAssertions(reqresp);

之后,您將需要更改commonAssertions方法,但是我敢肯定它會變得更簡單。 您還可能會發現代碼中的某些方法將請求和響應一起使用,因此將它們作為RequestAndResponse類中的方法是很自然的。

不知道您真正想要在這里實現什么。 但是,如果您想要“隨您走”收集請求,則可以使用peek()

final List<Request> requests = new ArrayList<>();

final List<Response> responses = bodies.stream()
    .map(Request::new)
    .peek(requests::add)
    .map(service::send)
    .collect(Collectors.toList());

commonAssertions(requests, responses);

.peek()與您在帖子主題中的意思.peek() 它需要一個Consumer ,它可以在管道中的任何步驟發生,並且此處消費者只是將“中間狀態”保存到列表中。

但是... peek()的javadoc特別提到了這一點:

對於並行流管道,可以在任何時間和線程中通過上游操作使元素可用,以調用該操作。 如果操作修改了共享狀態,則它負責提供所需的同步。

所以,好吧,我想...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM