英文:
Traverse complex object using Optional stream
问题
我想要迭代一个类似以下结构的嵌套对象列表:
Country {
String name;
State capital
List<State> states;
}
State {
String name;
City capital
List<City> cities;
}
City {
String name;
}
我正在使用以下代码来获取所有名称为'XYZ'的州的首府列表:
List<Country> countries = getCountriesList();
if (countries != null) {
countries.stream()
.filter(Objects::nonNull)
.filter(country -> CollectionUtils.isNotEmpty(country.getStates()))
.flatMap(country -> country.getStates().stream())
.filter(Objects::nonNull)
.filter(state -> "XYZ".equalsIgnoreCase(state.getName()))
.map(State::getCapital)
.filter(Objects::nonNull)
.map(capital -> capital.getName())
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
我如何重构这段代码以避免在每一步使用nonNull
?
英文:
I want to iterate a list with nested list of object similar to below
Country {
String name;
State capital
List<State> states;
}
State {
String name;
City capital
List<City> cities;
}
City {
String name;
}
I am using below code to get the capital list of all states with name 'XYZ'
List<Country> countries = getCountriesList();
if (countries != null) {
countries.stream()
.filter(Objects::nonNull)
.filter(country -> CollectionUtils.isNotEmpty(country.getStates()))
.flatMap(country -> country.getStates().stream())
.filter(Objects::nonNull)
.filter(state -> "XYZ".equalsIgnoreCase(state.getName()))
.map(State::getCapital)
.filter(Objects::nonNull)
.map(capital -> capital.getName())
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
How can I refactor this to avoid using non null at each step?
答案1
得分: 1
以下是翻译好的部分:
"我不确定为什么您想要避免使用 nonNull
。当前的做法非常可读,清晰地展现了您的意图。如果我真的要挑刺的话,我会去掉对空状态的过滤器,
.filter(country -> CollectionUtils.isNotEmpty(country.getStates()))
因为这已经在下一行的 flatMap
中处理了。此外,您可以使用 Java 9 的 .toList()
代替 collect
。
尽管如此,如果您真的想摆脱 nonNull
过滤器,您可以通过大量使用 Optional
来实现。大写字母和大写字母名称的空值检查可以通过 flat 映射到 Optional
的 stream
来完成,而州的空值检查可以与下一行的 "XYZ"
州名过滤器结合在一起。
这会得到以下结果:
countries.stream()
.filter(Objects::nonNull)
.flatMap(country -> country.getStates().stream())
.filter(state -> state != null && "XYZ".equalsIgnoreCase(state.getName()))
.flatMap(state -> Optional.ofNullable(state.getCapital()).stream())
.flatMap(capital -> Optional.ofNullable(capital.getName()).stream())
.toList();
第一个空值检查有点难以摆脱,除非您有一个已知没有州的国家:
public static final Country EMPTY = new Country("", null, List.of());
然后您可以使用 Objects.requireNonNullElse
来默认使用 EMPTY
国家:
countries.stream()
.flatMap(country -> Objects.requireNonNullElse(country, Country.EMPTY).getStates().stream())
但实际上,每次避免使用 nonNull
都会使代码变得更难阅读。"
英文:
I'm not sure why you would want to avoid using nonNull
. The current way of doing it is perfectly readable, and it clearly shows your intentions. If I really were to nitpick, I would remove the filter for empty states,
.filter(country -> CollectionUtils.isNotEmpty(country.getStates()))
since this is already covered by the flatMap
in the next line. Also, you can use .toList()
instead of collect
since Java 9.
That said, if you really want to get rid of the nonNull
filters, you can do so by using a lot of Optional
s. The capital and capital name null checks can be done by flat mapping to the optional's stream
, and the state null check can be combined with the "XYZ"
state name filter on the next line.
That gives us:
countries.stream()
.filter(Objects::nonNull)
.flatMap(country -> country.getStates().stream())
.filter(state -> state != null && "XYZ".equalsIgnoreCase(state.getName()))
.flatMap(state -> Optional.ofNullable(state.getCapital()).stream())
.flatMap(capital -> Optional.ofNullable(capital.getName()).stream())
.toList();
The first null check is a bit hard to get rid of, unless you have a known country that has no states:
public static final Country EMPTY = new Country("", null, List.of());
Then you could use Objects.requireNonNullElse
to default to the EMPTY
country:
countries.stream()
.flatMap(country -> Objects.requireNonNullElse(country, Country.EMPTY).getStates().stream())
Really though, with each nonNull
you avoid, you are just making the code harder to read.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论