如何用Java 8 Stream压缩嵌套的列表映射? [重复]

问题描述 投票:3回答:2

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

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

Item看起来像这样

public class Item {
    private String itemTag;
    private String itemName;
    private String itemType;
    private Integer itemStatus;
    private List<Item> items;
}

这不是最好的设计 - 我知道,但我无权改变这种设计。

我试图找到一种方法将这个结构扁平化为单个Stream并找到一个匹配ItemitemTag。使用此代码:

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();

但这只搜索项目列表的一个级别。如果我想要更深层次,我可以简单地做:

Item item = categories.stream()
                .flatMap(category -> category.getItems().stream())
                .flatMap(item->item.getItems().stream()))
                .filter(tagToFind.equals(item.getItemTag()))
                .findFirst();

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

java recursion java-stream nested-lists
2个回答
3
投票

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

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

使用这个flatMapRecursive()方法:

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

还有一件事需要考虑: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));
}

或者在使用之前对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));
}

2
投票

其他方式 :

public Item getFirstItemWithTag(List<Category> categories, String tag) {

        List<List<Item>> items = categories
                .stream()
                .map(Category::getItems)
                .collect(Collectors.toList());

        for(List<Item> items1 : items) {
            List<Item> itemsToAdd = items1.stream().filter(Objects::nonNull).collect(Collectors.toList());

            Optional<Item> first = itemsToAdd
                    .stream()
                    .filter(item -> item != null && tag.equals(item.getItemTag()))
                    .findFirst();

            if (first.isPresent()) {
                return first.get();
            }

            do {

                Stream<Item> itemStream = itemsToAdd
                        .stream()
                        .map(Item::getItems)
                        .flatMap(Collection::stream)
                        .filter(Objects::nonNull);

                first = itemsToAdd
                        .stream()
                        .filter(item -> item != null && tag.equals(item.getItemTag()))
                        .findFirst();

                if (first.isPresent()) {
                    return first.get();
                }

                itemsToAdd = itemStream
                        .collect(Collectors.toList());
            } while (!itemsToAdd.isEmpty());
        }


        return null;
    }

这也删除了Item的空条目,并且比在过滤之前收集Items的完整列表更快,因为它在发现时进行过滤。

© www.soinside.com 2019 - 2024. All rights reserved.