我对Java 8中的流是陌生的。我正在努力寻找如何使用流在列表上实现嵌套的for循环的方法。一个列表包含消息,消息中的字段之一是作者的ID(可悲的是,不是obj引用)。另一个列表是作者,作者中的一个字段是作者ID。该代码需要在作者列表中为消息列表中的每条消息找到匹配的作者。我拥有的有效Java代码(不使用流或lambda)如下所示,我的问题是如何使用流重写此代码?我尝试了几件事,我认为将需要一个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字段包含一些空数据。这要求我在流操作中使用过滤器来创建AuthorId,Name的映射。
答案 0 :(得分:1)
由于您要进行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());
);
答案 1 :(得分:1)
首先,对我来说,您当前的逻辑是错误的。如果第一次不满足该条件,并且对于所有消息,您将获得先前的作者或类似的无效结果,则它将破坏内部循环。
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())));
答案 2 :(得分:1)
尝试了上面的一些建议之后,我很快意识到作者数据中的某些对象没有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位响应者有所帮助?那是为了什么?
梅格
答案 3 :(得分:0)
您不需要流来处理消息,这里forEach更合适。尽管如此,流对于作者列表进行映射转换还是有用的。
您可以将toMap与3个参数一起使用,以避免作者列表中的作者重复(原始代码避免了这一点)。
您可以使用Optional来避免可能缺少身份验证,空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)
);