如何使用 Java 8 Stream 展平列表的嵌套映射?
How to flatten nested map of lists with Java 8 Stream?
我的结构如下所示:
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
并找到匹配 itemTag
的 Item
。使用此代码:
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();
效果很好。但是我正在尝试找到一种更具可扩展性的方法来做到这一点,它可以像嵌套列表一样深入。有没有一种有效的方法来做到这一点?
您需要一个单独的递归方法。你可以这样做:
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));
}
另一种方式:
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
的空条目,并且比在过滤之前收集 Item
的完整列表更快,因为它会在发现时进行过滤。
我的结构如下所示:
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
并找到匹配 itemTag
的 Item
。使用此代码:
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();
效果很好。但是我正在尝试找到一种更具可扩展性的方法来做到这一点,它可以像嵌套列表一样深入。有没有一种有效的方法来做到这一点?
您需要一个单独的递归方法。你可以这样做:
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));
}
另一种方式:
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
的空条目,并且比在过滤之前收集 Item
的完整列表更快,因为它会在发现时进行过滤。