如何检查对象是否在 Optional<Object> 映射中 - Java

How to check if a object is in Optional<Object> map - Java

pojoMappedByName 包含 pojotest 和许多其他元素。如何检查 pojoTest 是否在 pojoMappedByName 中?

我尝试使用 if(pojoMappedByName.containsValue(pojotest)),但它返回错误。是因为类型是 Optional<PojoClass> 而不是类型 PojoClass

PojoClass pojotest = new PojoClass();
PojoClass.setName("Tommy");

Map<String, Optional<PojoClass>> pojoMappedByName;

我想尽可能避免使用 for 循环

您可以重写 hashCode() 和 equals() 方法,并让 equals 在 PojoClass 中发挥独特的作用,使您的比较像 id

@Override
public boolean equals(Object o) {
    if (o == this)
        return true;
    if (!(o instanceof PojoClass))
        return false;
    PojoClass other = (PojoClass)o;
    return this.id == other.id;
}

一种可能的解决方案是将其包装在 Optional 中,如下所示:

 assertTrue(pojoMappedByName.containsValue(Optional.of(pojotest)));

可选的不应该在那里。 Optional 最适合用作终端流操作的 return 类型。有点争议,但可以接受1,作为return类型的非流相关方法。在其他任何地方使用它都是一个错误——它不应该是一个字段的类型,它不应该是一个方法参数的类型,它不应该出现在任何泛型类型中。 List<Optional<String>>,等等 - 没有这些。

您偶然发现了一个实际例子来说明为什么会这样。概括一下原则:Optional 不像泛型那样是类型系统的一部分,并且相对于你的类型是正交的 'wrapped'。换句话说,StringOptional<String> 就像枪支和祖母一样不同,这是一个问题,因为在你的脑海中他们感觉很相似。即使在可选项上调用 .equals 本身也是可疑的,因为 Optional 试图捕捉 'I convey the result of a particular invocation of a stream terminal' 的语义含义,我们可以就 'the way this terminal ended' 的概念是否可以进行长期的哲学辩论甚至被认为等于一个不同的运行,而恰好产生了与结果相同的对象。

泛型对任何特定类型都有 4 种不同的概念:

  • List<Number>
  • List<? extends Number>
  • List<? super Number>
  • List(原始/传统)

可选在类型系统中的正确集成需要类似的处理:例如,您希望能够编写一个接受 List<Optional<String>>List<String> 的方法。如果该方法总是将字符串中的元素视为可选的(如检查 NONE 等),但如果它写入列表,则只写入实际的字符串(从不写入 Optional.NONE),那么此方法可以处理任何一种形式。

这类似于 List<? super Number> 可以调用 .add(Number) 的方式,但如果它调用 .get(0),那是对象类型而不是数字类型。

Java不支持这个

没有办法写这样的方法

因此,不要集成可选的 - 鉴于您无法编写该方法,您不希望 List<Optional<String>> 代码库中的任何地方。相反,将 Optional 视为一种转瞬即逝的数据类型:如果您从任何地方获得一个 Optional,'unroll' 那个可选的当场。不要将它存储在一个字段中,不要将它传递给另一个方法。您应该考虑的唯一稍微不那么短暂的想法是逐字 return 它,将解包可选的责任放在调用者身上。这是可以接受的。


如果您想加重这个错误并使您的代码库变得更糟,这将起作用。但我强烈反对!

pojoMappedByName.containsValue(Optional.of(pojotest))

(Optional的equals方法定义为:NONE等于NONE,如果a.equals(b)成立,则任意2个SOME彼此相等,其中ab 是包含在两个可选项中的实际对象,这就是它起作用的原因。虽然这不是一个好主意,但更像是 'its the least worst option amongst all available interpretations of what equality might mean between optionals'.

[1] java 核心库和主要库中的很多方法都不是那样工作的。例如,如果您认为 Optional<T> 是 return 事实上正确的东西,对于可以 return 概念 'not relevant' 或 'not found' 的方法,那么显然 java.util.Map.get(key) 方法应该是 returning 一个 Optional<V>。它不会,也永远不会,因为它不是向后兼容的更改。鉴于那里有大量代码,因此不能仅仅改变范式而且可能永远不会,这个想法有很多阻力,并且生活在一个任何给定方法在概念上都可以 return 'not found', 'not available', 等等 - 然后会强制您首先检查文档以查看 returns Tnull 是否表示未找到,或者 returns Optional<T>。将这种二分法强加于生态系统自然是有争议的。我还没有听说过一个可行的建议去到一个地方 Optional 已经替换 null for all method return values in all 主要图书馆(和核心图书馆)。在一个存在之前,它将一直存在争议,我强烈建议您不要去那里,并避免使用 Optional<T> 作为方法的 return 值,除非它们与流相关。然而,一些图书馆和少数(但相当少数)编程团队 正在 这样做。与在例如中使用 Optional 相比List<Optional<T>> 或作为方法参数类型:只有极少数开发团队在这样做,而且没有风格指南建议这样做。

你可以获取HashMap的所有值,使用stream filter操作查看你搜索到的pojo是否存在,使用count查看搜索到的pojo的数量,如果为0则不存在,否则超过一个pojo存在于您的 HashMap 中,请尝试正确覆盖 PojoClass 中的 equals 方法。

@Data
@EqualsAndHashCode
class PojoClass {
private String name;
// getter + setter + equals +hashcode : generated by Lombok
}

public class Bounder {

static boolean pojoCounter(Map<String, Optional<PojoClass>> pojoMappedByName, PojoClass pojo) {
    long x = pojoMappedByName.values().stream().
            filter(e -> e.get().equals(pojo)).count();
    if (x == 0) {
        return false;
    } else
        return true;
}

public static void main(String[] args) throws IOException {
    PojoClass NotAPojo = new PojoClass();
    NotAPojo.setName("NotAPojo");

    PojoClass pojotest = new PojoClass();
    pojotest.setName("Tommy");
    PojoClass pojotest1 = new PojoClass();
    pojotest1.setName("Hammy");
    PojoClass pojotest2 = new PojoClass();
    pojotest2.setName("Mammy");
    PojoClass pojotest3 = new PojoClass();
    pojotest3.setName("Kammy");

    Map<String, Optional<PojoClass>> pojoMappedByName = new HashMap<String, Optional<PojoClass>>();

    pojoMappedByName.put("0", Optional.of(pojotest));
    pojoMappedByName.put("1", Optional.of(pojotest1));
    pojoMappedByName.put("2", Optional.of(pojotest2));
    pojoMappedByName.put("3", Optional.of(pojotest3));

    System.out.println("pojoMappedByName , pojotest :  exists ?  " + pojoCounter(pojoMappedByName, pojotest));
    System.out.println("pojoMappedByName , pojotest1   exists ?  " + pojoCounter(pojoMappedByName, pojotest1));
    System.out.println("pojoMappedByName , pojotest2   exists ?  " + pojoCounter(pojoMappedByName, pojotest2));
    System.out.println("pojoMappedByName , pojotest3   exists ?  " + pojoCounter(pojoMappedByName, pojotest3));
    System.out.println("pojoMappedByName , NotAPojo    exists ?  " + pojoCounter(pojoMappedByName, NotAPojo));

}
}

输出:

pojoMappedByName , pojotest : exists ? true

pojoMappedByName , pojotest1 exists ? true

pojoMappedByName , pojotest2 exists ? true

pojoMappedByName , pojotest3 exists ? true

pojoMappedByName , NotAPojo exists ? false