Java 8获取列表中的所有元素

13

我有一个对象列表,每个对象都返回List<String>。如何使用Java 8流仅获取一个List<String>

Contact类具有以下方法;

public List<String> getSharedFriendsIds() {
    return sharedFriendsIds;
}

我有

List<Contact> contactsList;

我尝试的是

List<String> sharedContacts = contactsList.stream()
    .map(Contact::getSharedFriendsIds)
    .sequential()
    .collect(Collectors.toList());

但是上述代码并不会返回 List<String>,而是返回 List<List<String>>,这不是我想要的。


3
为什么需要使用.sequential() - Tagir Valeev
3个回答

15

你应该使用.flatMap()方法从主列表contactsList中每个Contact对象所包含的sharedFriendsIds列表中创建一个单一的列表。请检查;

List<String> sharedContacts = contactsList.stream()
        .map(Contact::getSharedFriendsIds)
        .filter(Objects::nonNull)
        .flatMap(Collection::stream)
        .sorted().collect(Collectors.toList());

.filter() 调用是为了处理列表中任何 ContactsharedFriendsIds == null 的情况,因为这会导致下一行出现 NPE,我们应该过滤掉这些。还有其他方法可以实现这个目的,比如:

- Optional

    List<String> sharedContacts = contactsList.stream()
            .flatMap(contacts -> Optional.ofNullable(contacts.getSharedFriendsIds())
                    .map(Collection::stream).orElseGet(Stream::empty))
            .sorted().collect(Collectors.toList());

  Where the filtering of null `sharedFriendsIds` are done in such a way that 
 they are absorbed into the `flatMap` logic as empty streams.

- emptyIfNull()

您可以从apache.commons导入集合依赖项,并使用以下方式调用CollectionUtils.emptyIfNull方法:

    public static <T> Stream<T> collectionAsStream(Collection<T> collection) {
        return emptyIfNull(collection).stream();
    }

然后从原始流中调用它,如下所示:

     List<String> sharedContacts = contactsList.stream()
             .map(Contact::getSharedFriendsIds)
             .flatMap(Foo::collectionAsStream)
             .sorted().collect(Collectors.toList());

我想你在排序逻辑中使用了 .sequential,我猜你应该使用 .sorted 方法,因为 sequential 是用于触发非并行使用的,而这已经是 Stream 的默认配置。


2
@tsingh 最好让 getSharedFriendsIds() 返回一个空流而不是 null - Stuart Marks
3
你调用了两次 getSharedFriendsIds() 方法。可以通过先执行 .map(Contact::getSharedFriendsIds),然后再执行 .filter 步骤来轻松避免这种情况。你甚至可以强制转换过滤和扁平映射步骤,例如 .map(Contact::getSharedFriendsIds) .flatMap(list->list!=null? list.stream(): null),但这只是品味问题... - Holger

5
这里没有必要使用 .sequential(),流默认是按顺序执行的。
List<String> sharedContacts = contactsList.stream()
        .map(Contact::getSharedFriendsIds)
        .filter(Objects::nonNull)
        .flatMap(Collection::stream)
        .collect(Collectors.toList());

按照自然顺序:

List<String> sharedContacts = contactsList.stream()
        .map(Contact::getSharedFriendsIds)
        .filter(Objects::nonNull)
        .flatMap(Collection::stream)
        .sorted().collect(Collectors.toList());

2

我发现一个非常有用的模式是让父类(在这种情况下是Contact类)创建并返回子对象流(在这种情况下是共享好友ID):

public class Contact {

    private List<String> sharedFriendsIds;

    public Stream<String> sharedFriendsIds() {
        return sharedFriendsIds == null ? Stream.empty() : sharedFriendsIds.stream();
    }

    public List<String> getSharedFriendsIds() {
        return sharedFriendsIds;
    }
}

惯例是将返回流的方法命名为正在流式传输的属性。该方法已经包含了空值检查。

然后,获取所有联系人的共享好友 ID 就变得更加容易:

List<String> sharedContacts = contactsList.stream()
        .flatMap(Contact::sharedFriendsIds)
        .collect(Collectors.toList());

您需要使用flatMap()来将子列表的元素“展平”为单个列表,否则您会得到一个流列表。
注意1:您不需要使用sequential(),因为在联系人列表上使用stream()已经返回了一个顺序流。
注意2:如果您希望最终列表排序,则应在流上使用sorted()
List<String> sharedContacts = contactsList.stream()
        .flatMap(Contact::sharedFriendsIds)
        .sorted().collect(Collectors.toList());

1
这是一个不错的清理混乱lambda符号的方法,我也想过,但这种方法只适用于这种逻辑,并且有些过度了,如果这样的映射频繁出现,我会同意使用。另外,我认为他的意思是要对结果列表进行排序,连续使用是他的错误。 - buræquete
1
@bureaquete 嗯,你可以把流方法看作是一个getter。通常情况下,你总是会编写getter,无论你是否在逻辑中使用它们。同意sequential() vs sorted()的问题,我也相信OP想要一个排序后的列表。 - fps

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接