布尔表达式的取反与异或

5

我有这个:

// returns true if both are equal (independent of scale) and also checks against null
public static boolean isEqual(BigDecimal val1, BigDecimal val2) {
        // 1. check: both will be null or both will be non-null.
        if (val1 != null ^ val2 != null) return false;
        // 2. check: if not null, then compare if both are equal
        return !(val2 != null && val1.compareTo(val2) != 0);
    }

我想将布尔表达式合并为一个。因此我使用了以下代码:

public static boolean isEqual(BigDecimal val1, BigDecimal val2) {
    return !(val1 != null ^ val2 != null) && !(val2 != null && val1.compareTo(val2) != 0);
}

然而,我担心这是否正确。这个正确吗?能否简化或缩短?

借助答案的帮助,简化的解决方案是:

// returns true, if both are null or both are equal 
// (independent of their numbers scales)
public static boolean isEqual(BigDecimal val1, BigDecimal val2) {
    return val1 == null ? val2 == null : val2 != null && val1.compareTo(val2) == 0;
}

所以你想要检查两个 BigDecimal 是否相等? - Dani Mesejo
2
"我想将布尔表达式合并成一个。" -- 不要这样做。我完全理解想要更“紧凑”的代码的愿望,但编译器会将它们都转换为相同的字节码,所以你应该坚持让你的代码尽可能易于阅读(因此易于调试和维护)。 - Jordan
“正确”是什么意思?它是否实现了您想要的逻辑或功能? - TylerH
@TylerH 是的,就像已经回答的那样。 - nimo23
@nimo23 抱歉,那没有意义。我所指的“它”是你问题中的代码。如果你问题中的代码能够按照你的预期工作,那么它就是“正确”的。否则你需要澄清你的意思(否则问题有被标记为不清楚或基于个人观点而被关闭的风险)。 - TylerH
是的,我问题中的代码已经是正确的了,但太复杂了。简化后的解决方案使它更简单。 - nimo23
3个回答

7

使用 ^ 作为逻辑异或运算符非常不寻常。虽然它可以工作,但出于可读性的考虑,我建议避免使用。 != 是一个很好的替代品。

return !((val1 != null) != (val2 != null)) && !(val2 != null && val1.compareTo(val2) != 0);

现在你可以用==代替双重否定。很好。
return ((val1 != null) == (val2 != null)) && !(val2 != null && val1.compareTo(val2) != 0);

您还可以通过德摩根定律分配剩余的!

return ((val1 != null) == (val2 != null)) && (val2 == null || val1.compareTo(val2) == 0);

虽然这样做已经好了一些,但说实话我仍然觉得它不太方便。我会选择更直接的方法,即使它不是一个单行语句:

if (val1 == null) {
    return val2 == null;
}
else {
    return val2 != null && val1.compareTo(val2) == 0;
}

你可以使用三元运算符来代替if/else语句。哪种更易读取取决于个人喜好:
return val1 == null
    ? val2 == null
    : val2 != null && val1.compareTo(val2) == 0;

您提到需要使用compareTo()。对于其他可能阅读此答案的人,如果不必使用compareTo(),我建议使用equals()代替。

if (val1 == null) {
    return val2 == null;
}
else {
    return val1.equals(val2);
}

然后,恰巧地,您根本不需要编写此方法。内置的 Objects.equals() 方法正是这样做的:如果两个对象相等或都为 null,则返回 true。

1
a.equals(b) 的行为与 Objects.equals(a, b) 相同,这不是我想要的。我想检查两者是否相等(与比例无关),并检查 null 值。(因此,如果两者都为 null,则应将两者视为相等)。 - nimo23
@Rogue 我不明白:我想使用 boolean isEqual(BigDecimal val1, BigDecimal val2) {return val1 == null ? val2 == null : val2 != null && val1.compareTo(val2) == 0;}。你觉得怎么简化这个代码? - nimo23
@nimo23 我认为这个答案的最后一个示例/代码是指你的评论,而不是你的评论。 - user85421

2

您可以使用Comparator.nullsFirst(或Comparator.nullsLast):

public static boolean isEqual(BigDecimal val1, BigDecimal val2) {
    return Comparator.nullsFirst(BigDecimal::compareTo).compare(val1, val2) == 0;
}

例子:

    System.out.println(isEqual(null, null));
    System.out.println(isEqual(null, new BigDecimal(1.0)));
    System.out.println(isEqual(new BigDecimal(1.0), null));
    System.out.println(isEqual(new BigDecimal(1.0), new BigDecimal(1.0)));

输出

true
false
false
true

1
嗯,我喜欢你的解决方案。然而,我认为被接受的答案的方法更适合覆盖equals()/hashCode()方法。不需要创建对象比较器等,只需使用布尔运算符即可,速度应该更快。 - nimo23

2

如果规模不是问题,我会在这里使用三元运算符,并利用Object#equalsnull返回false的特性:

//Basically Objects#equals at this point
public static boolean isEqual(BigDecimal val1, BigDecimal val2) {
    return val1 == null ? val2 == null : val1.equals(val2);
}

因此,在每种情况下:
[1: null, 2: null]: true
[1: null, 2:  num]: false
[1: num,  2: null]: false
[1: num,  2:  num]: Object#equals

然而,正如你所注意到的,你希望无论缩放如何都能进行比较。因此,我们需要包含另一个空值检查,因为如果你传递null#compareTo会抛出NPE异常:

public static boolean isEqual(BigDecimal val1, BigDecimal val2) {
    return val1 == null
            ? val2 == null
            : val2 != null && val1.compareTo(val2) == 0;
}

因此,在每种情况下(再次强调):
[1: null, 2: null]: true
[1: null, 2:  num]: false
[1: num,  2: null]: false
[1: num,  2:  num]: BigDecimal#compareTo

我认为这段代码更易读,但仍有些难以操作。此时,一份精简明了的注释(或者按照其他回答所述分行)可能比重写代码更好。

谢谢,遗憾的是JDK没有在compareTo()中提供空值检查。 - nimo23
1
这实际上不应该发生,因为你正在返回一个标量,它代表两个值之间的差异。没有一个好的数字可以显示 14null 之间的差异,既不会与其他类似的值冲突,也不会引入魔法值。 - Rogue
但是使用BigDecimal覆盖equals() / hashcode()实际上有点冒险,因为某些人需要知道在使用Objects.equals(1.0, 1.000)时默认情况下1.0!= 1.000,然后我必须处理NPE…所以当覆盖BigDecimal属性的equals/hashCode时,上面的代码并不容易实现。 - nimo23
第一个代码片段就是 Objects#equals 的实现,不需要重新实现它。 - user85421
@CarlosHeuberger 很好的观点,我会保留下来以供后人参考。另一个解决方案可能是将比例尺设置为匹配,但我不喜欢那种解决方案的副作用。 - Rogue
我不喜欢改变比例的想法,这样很容易搞砸。 - user85421

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