簡體   English   中英

嵌套循環到Java流拼圖

[英]Nested Loop to Java Stream Puzzle

我是Java 8中的新手。我正在努力弄清楚如何使用流在列表上實現嵌套for循環。 一個列表包含消息,消息中的一個字段是作者的id(不幸的是obj引用)。 另一個列表是作者,作者中的一個字段是作者ID。 代碼需要在消息列表中的每個消息的作者列表中找到匹配的作者。 我有的工作Java代碼(不使用stream或lambdas)如下,我的問題是如何使用流重寫它? 我嘗試了幾件事,我認為需要一個flatMap和一個過濾器,但我似乎無法做到這一點。

public void showAuthors(List<Message> messages, List<Authors> authors) {
    String authorName=null;
    for (Message message : messages) {
        int authorId = message.getAuthorId();
        for (Authors author : authors) {
            if (author.getId() == authorId)
                authorName = author.getFullName();
                break;
        }
        System.out.println("Message id is " + message.getId() + 
                           " author is " + authorName);
    }
}

我在這里看了一些類似的問題,但我似乎無法做到正確。

任何幫助表示贊賞!

=============================================

更新:作者列表具有Id字段的一些空數據。 這要求我使用帶有stream操作的過濾器來創建AuthorId,Name的映射。

由於您正在進行mxn比較,因此最好在authorId -> author之間創建一個Map,以避免對作者進行O(n)查找:

Map<Integer, Author> authorMaps = authors.stream().collect(toMap(Author::getId, Function.identity()));

現在您可以獲取每條消息的作者,如下所示:

messages.stream().filter(m -> authorMaps.containsKey(m.getAuthorId()))
        .forEach(m -> 
            System.out.println("Message id is " + m.getId() + 
                           " author is " + authorMaps.get(m.getAuthorId()). getFullName());
        );

首先,對我來說,你當前的邏輯是錯誤的。 如果第一次不滿足條件,並且您獲得前一作者的所有消息或一些無效結果,它會破壞內部循環。

Message id is 1 author is AuthOne
Message id is 2 author is AuthOne
Message id is 4 author is AuthOne

這是糾正過的。

public static void showAuthors(List<Message> messages, List<Author> authors) {
    String authorName = null;
    for (Message message : messages) {
        int authorId = message.getAuthorId();
        for (Author author : authors) {
            if (author.getId() == authorId) {
                authorName = author.getFullName();
                break;
            }
        }
        System.out.println("Message id is " + message.getId() + " author is " + authorName);
    }
}

只有在找到第一個Author后才能打破循環。 這是具有線性時間復雜度的等效stream對應物。

Map<Integer, String> authorsById = authors.stream()
        .collect(Collectors.toMap(Author::getId, Author::getFullName));
Map<Integer, String> authorNameAgainstId = messages.stream()
        .collect(Collectors.toMap(Message::getId, m -> authorsById.get(m.getAuthorId())));

在嘗試了上面的一些建議之后,我很快意識到作者的數據有一些沒有Id的對象,只是名字(顯然它們有雙重用途......這不是我的設計!)。

所以,我根據上面答案的幫助/提示寫了以下內容:

public void showAuthors(List<Message> messages, List<Reference> authors) {
Map<Integer, String> authorsById = authors.stream()
    .filter(author -> !(author.getId() == null))
    .filter(name -> !(name.getFullName() == null))
    .collect(Collectors.toMap(Reference::getId, Reference::getFullName));

messages.forEach(
    message -> System.out.println("Message id is " + message.getId() + 
              " Author is " + authorsById.get(message.getSenderId())));

這個解決方案有什么問題嗎?

另外,我是stackoverflow的新手(我相信你可以告訴我)...有沒有辦法可以給所有3個響應者一些幫助? 這是什么上升的意思?

梅格

你不需要用於消息處理的流,這里forEach更合適。 然而,流對於作者列表來映射轉換很有用。

您可以使用帶有3個參數的toMap來避免作者列表中可能的作者重復(原始代碼避免了這一點)。

您可以使用Optional來避免可能缺少authos,null id等。每次.map返回null,Optional變為Optional :: Empty,因此對它的進一步處理被跳過到終端操作,ifPresence只是跳過空選項。

Map<Integer, String> authorNames=authors.stream()
    .collect(Collectors.toMap(
        Author::getId,
        Author::getFullName,
        (oldValue, newValue) -> oldValue));

messages.forEach(
    m -> Optional.of(m)
        .map(Message::getAuthorId)
        .map(authorNames::get)
        .map(name -> "Message id is " + m.getId() + " author is " + name)
        .ifPresent(System.out::println)
);

暫無
暫無

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

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