使用Optional流遍历复杂对象

huangapple go评论55阅读模式
英文:

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&lt;State&gt; states;
}

State {
  String name;
  City capital
  List&lt;City&gt; cities;
}

City {
  String name;
}

I am using below code to get the capital list of all states with name 'XYZ'

List&lt;Country&gt; countries = getCountriesList();

if (countries != null) {
    countries.stream()
            .filter(Objects::nonNull)
            .filter(country -&gt; CollectionUtils.isNotEmpty(country.getStates()))
            .flatMap(country -&gt; country.getStates().stream())
            .filter(Objects::nonNull)
            .filter(state -&gt; &quot;XYZ&quot;.equalsIgnoreCase(state.getName()))
            .map(State::getCapital)
            .filter(Objects::nonNull)
            .map(capital -&gt; 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 -&gt; CollectionUtils.isNotEmpty(country.getStates()))

因为这已经在下一行的 flatMap 中处理了。此外,您可以使用 Java 9 的 .toList() 代替 collect

尽管如此,如果您真的想摆脱 nonNull 过滤器,您可以通过大量使用 Optional 来实现。大写字母和大写字母名称的空值检查可以通过 flat 映射到 Optionalstream 来完成,而州的空值检查可以与下一行的 &quot;XYZ&quot; 州名过滤器结合在一起。

这会得到以下结果:

countries.stream()
    .filter(Objects::nonNull)
    .flatMap(country -&gt; country.getStates().stream())
    .filter(state -&gt; state != null &amp;&amp; &quot;XYZ&quot;.equalsIgnoreCase(state.getName()))
    .flatMap(state -&gt; Optional.ofNullable(state.getCapital()).stream())
    .flatMap(capital -&gt; Optional.ofNullable(capital.getName()).stream())
    .toList();

第一个空值检查有点难以摆脱,除非您有一个已知没有州的国家:

public static final Country EMPTY = new Country(&quot;&quot;, null, List.of());

然后您可以使用 Objects.requireNonNullElse 来默认使用 EMPTY 国家:

countries.stream()
    .flatMap(country -&gt; 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 -&gt; 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 Optionals. 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 &quot;XYZ&quot; state name filter on the next line.

That gives us:

countries.stream()
    .filter(Objects::nonNull)
    .flatMap(country -&gt; country.getStates().stream())
    .filter(state -&gt; state != null &amp;&amp; &quot;XYZ&quot;.equalsIgnoreCase(state.getName()))
    .flatMap(state -&gt; Optional.ofNullable(state.getCapital()).stream())
    .flatMap(capital -&gt; 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(&quot;&quot;, null, List.of());

Then you could use Objects.requireNonNullElse to default to the EMPTY country:

countries.stream()
    .flatMap(country -&gt; Objects.requireNonNullElse(country, Country.EMPTY).getStates().stream())

Really though, with each nonNull you avoid, you are just making the code harder to read.

huangapple
  • 本文由 发表于 2023年3月9日 16:30:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/75682065.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定