从HashMap中提取非空值和非空字符串的Java 8代码

16

让我们考虑下面的HashMap

HashMap<String, String> map = new HashMap<String, String>();

我有一个类似下面的map:

map.put("model", "test");

目前,如果我想从映射中获取值,我会这样做:

if(map!=null){
 if(map.get("model")!=null && !map.get("model").isEmpty()){
   //some logic
 }
}

使用Java 8中的Optional或Lambda表达式是否有更好的方法来实现上述条件?


1
这个问题在我看来似乎主要基于个人观点。我会编辑关于“更好的方法”的部分。 - Michael Easter
5个回答

13

首先,您的 Map 对象不应该为 null。永远不要为 null。它可能是空的,但没有理由为 null。因此第一个 null 检查可以省略。

现在,不幸的是,Java 没有这样的实用方法,但是一些常用的库(如 Apache Commons、Guava 等)有这个方法,或者您可以自己编写,这样就变成了:

String model = map.get("model");
if (!Strings.isEmptyOrNull(model)) {
    // do somthing
}

使用 Optional 来包裹可空值作为逻辑的一部分被认为是一种反模式。Optional 的设计是用作返回类型的。因此,我不建议在此处使用它。

另外请注意,您似乎正在使用一个 Map 来存储对象的属性。如果是这样,请考虑定义一个具有类型属性的真正类,而不是使用 Map。


你能提供一个链接,以便进一步讨论在这种情况下“Optional”是反模式吗?我之前也读过,认为提供一个链接对读者会很有帮助。 - Zabuzard
1
https://www.youtube.com/watch?v=Ej0sss6cq14,https://dev59.com/ymAg5IYBdhLWcg3wjrgZ#23464794, - JB Nizet
关于 Map 不为空的问题,你并不知道。显示实例化 Map 的那一行可能是一个字段,它不是只读的,并且它可能在其他地方被改变了。 - LordWilmore
2
@LordWilmore 我的观点是它 不应该 为 null。如果一个 HashMap 为 null,那就是设计上的错误。因此,如果你的设计是优秀的,HashMap 永远不会为 null,所以你不需要关心这一点:如果 HashMap 为 null,那么这是一个 bug,并且抛出 NullPointerException 是正确的做法。 - JB Nizet
@JBNizet非常同意,这只是一种观察,但这个地方充满了人们询问有关糟糕代码的问题。 - LordWilmore
同意 Map 不应该为 null。 - Chathura Buddhika

10

不确定为什么您在创建地图后立即检查地图是否为null,但是这里有:

Optional.ofNullable(map)
    .map(m -> m.getOrDefault("model", "")) // Use an empty String if not present
    .filter(s -> !s.isEmpty())             // Filter all empty values
    .ifPresent(valueString -> {            // Check if value is present
        // Logic here
});

或者用一行文字表示:

Optional.ofNullable(map).map(m -> m.getOrDefault("model", "")).filter(s -> !s.isEmpty()).ifPresent(valueString -> {
        // Logic here
});

如果你想返回某些内容,请将ifPresent 替换为map,例如:Optional中包含你计算出来的任何内容。


我正在将JSON请求正文转换为映射,有时会出现一些内部JSON对象缺失的情况,因此我在这里检查了映射是否为空。 - ppb
@ppb 请查看jackson https://github.com/FasterXML/jackson,以处理将JSON转换为Java对象的操作。 - Bob Brinks

4
如果您对Optional方法感兴趣,
您可以将map.get("model")的值包装在Optional.ofNullable中,并使用Predicate<String> value ->!value.isEmpty()进行筛选工作。
if (isNull(map)) { // import static java.util.Objects.isNull;
    return;        // to minimise nesting
}

Optional.ofNullable(map.get("model"))
        .filter(value -> !value.isEmpty())
        .ifPresent(value -> { ... });

2
这比原始的方法更加复杂。 - developer_hatch
你可能想在这种情况下使用 map.getOrDefault,以防键不存在,否则您将会在该 filter 中讽刺地得到一个 NullPointerException - smac89
2
Objects.isNull(map)map == null更冗长。 - Klitos Kyriacou
Optional#filter 只有在存在 Predicate 函数时才会应用它,否则会返回一个Optional。所以我认为这是可以的。请参见 Optional#filter - Zabuzard
@KlitosKyriacou,是的,但使用静态导入对我来说更易读。 - Andrew Tobilko

0
如果您在示例代码中声明了map,那么它不会是null,您无需检查它。如果您想确保,可以添加一个断言:
assert map != null;

假设您正在测试空字符串,可能的方法是在键不存在时使用空字符串作为默认值:

if (!map.getOrDefault("model", "").isEmpty()) {
    ...
}

我认为很遗憾的是,Map没有添加一种方法来返回一个Optional而不是null,以表示键不存在的情况。类似于:

map.getOptional("model").filter(v -> !v.isEmpty()).ifPresent(v -> {
    ...
}

虽然可选项已经被添加,但是旧的API中仍有一些方法返回null来表示“不存在”,而这些方法并没有得到很好的重构。


!map.getOrDefault("model", "").isEmpty() 的问题在于,如果 map 包含映射 "model" -> null,它将抛出 NPE,而原始 OP 代码可以防止这种情况的发生。 - Alexis C.
没错。我读了 OP 的代码,它检测的是缺失的键而不是空值。如果该映射不应该包含空值,那么这仍然是一个合理的解决方案。但我同意它并不能防止所有 NPEs。 - sprinter

0
你也可以尝试使用containsKey方法。例如:
HashMap<String, String> map = new HashMap<String, String>();
map.put("model", "test");

 if(map.containsKey("model")){
   //some logic
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接