I have a structure that looks like this:
public class Category {
private String tag;
private String name;
private String description;
private List<Item> items;
}
and Item
looks like this
public class Item {
private String itemTag;
private String itemName;
private String itemType;
private Integer itemStatus;
private List<Item> items;
}
It's not the best design - I know, but I have no power to change that design.
I'm trying to find a way to flatten this structure to a single Stream
and find an Item
with matching itemTag
. Using this code:
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();
But this only searches one level of the item list. If I want to go a level deeper I can simply do :
Item item = categories.stream()
.flatMap(category -> category.getItems().stream())
.flatMap(item->item.getItems().stream()))
.filter(tagToFind.equals(item.getItemTag()))
.findFirst();
Which works fine. But I'm trying to find a more scalable way of doing this where it can go as deep as the nested lists go. Is there an efficient way of doing this?
You need a separate method for the recursion. You can do it like this:
Optional<Item> item = categories.stream()
.flatMap(category -> category.getItems().stream())
.flatMap(MyClass::flatMapRecursive)
.filter(i -> tagToFind.equals(i.getItemTag()))
.findFirst();
Use this flatMapRecursive()
method:
public Stream<Item> flatMapRecursive(Item item) {
return Stream.concat(Stream.of(item), item.getItems().stream()
.flatMap(MyClass::flatMapRecursive));
}
One more thing to consider: The flatMapRecursive()
method does no null checks, so every item need at least an empty list, otherwise you will get a NullPointerException
.
If null
values are possible for the items
you can prevent this using an Optional
:
public Stream<Item> flatMapRecursive(Item item) {
return Stream.concat(Stream.of(item), Optional.ofNullable(item.getItems())
.orElseGet(Collections::emptyList)
.stream()
.flatMap(MyClass::flatMapRecursive));
}
Or doing the null check of items
before using it:
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));
}