如何使用 Java 8 Stream 展平嵌套的列表映射?

Var*_*ári 6 java recursion nested-lists java-stream

我有一个看起来像这样的结构:

public class Category {
    private String tag;
    private String name;
    private String description;
    private List<Item> items;
}

Run Code Online (Sandbox Code Playgroud)

Item看起来像这样

public class Item {
    private String itemTag;
    private String itemName;
    private String itemType;
    private Integer itemStatus;
    private List<Item> items;
}
Run Code Online (Sandbox Code Playgroud)

这不是最好的设计——我知道,但我无权改变那个设计。

我试图找到一种方法将这个结构展平为一个单一的Stream并找到一个Item匹配的itemTag. 使用此代码:

String tagToFind = "someTag";
List<Category> categories = getCategoriesList(); // <-- returns a list of Category
Item item = categories.stream()
                .flatMap(category -> category.getItems().stream())
                .filter(tagToFind.equals(item.getItemTag()))
                .findFirst();

Run Code Online (Sandbox Code Playgroud)

但这仅搜索项目列表的一级。如果我想更深入,我可以简单地做:

Item item = categories.stream()
                .flatMap(category -> category.getItems().stream())
                .flatMap(item->item.getItems().stream()))
                .filter(tagToFind.equals(item.getItemTag()))
                .findFirst();
Run Code Online (Sandbox Code Playgroud)

哪个工作正常。但是我试图找到一种更具可扩展性的方法来做到这一点,它可以像嵌套列表一样深入。有没有一种有效的方法来做到这一点?

Sam*_*ipp 5

您需要一个单独的递归方法。你可以这样做:

Optional<Item> item = categories.stream()
        .flatMap(category -> category.getItems().stream())
        .flatMap(MyClass::flatMapRecursive)
        .filter(i -> tagToFind.equals(i.getItemTag()))
        .findFirst();
Run Code Online (Sandbox Code Playgroud)

使用这个flatMapRecursive()方法:

public Stream<Item> flatMapRecursive(Item item) {
    return Stream.concat(Stream.of(item), item.getItems().stream()
            .flatMap(MyClass::flatMapRecursive));
}
Run Code Online (Sandbox Code Playgroud)

还要考虑的一件事:该flatMapRecursive()方法不进行空检查,因此每个项目至少需要一个空列表,否则您将获得一个NullPointerException.

如果您可以使用null以下值items来防止这种情况发生Optional

public Stream<Item> flatMapRecursive(Item item) {
    return Stream.concat(Stream.of(item), Optional.ofNullable(item.getItems())
            .orElseGet(Collections::emptyList)
            .stream()
            .flatMap(MyClass::flatMapRecursive));
}
Run Code Online (Sandbox Code Playgroud)

或者items在使用之前做空检查:

public Stream<Item> flatMapRecursive(Item item) {
    if (item.getItems() == null) {
        return Stream.empty();
    }
    return Stream.concat(Stream.of(item), item.getItems().stream()
            .flatMap(MyClass::flatMapRecursive));
}
Run Code Online (Sandbox Code Playgroud)

  • 使用 ```MyClass::flatMapRecursive``` 非常简洁。 (2认同)