比较器:比较 2 个对象与可以为空的字段

Comparator: Comparing 2 objects with field which can be null

我需要通过实现默认比较器的典型比较方法来编写 Comparator。这是由于我需要实现的接口。

我的对象是具有 Integer 字段 vintage 的产品,可以是 null。代码如下:

@Override
public int compare ( IProduct product1, IProduct product2 ) throws ProductComparisonException
{

    if ( product1 == null && product2 == null )
    {
        return 0;
    }
    else if ( product1 == null && product2 != null )
    {
        return -1;
    }
    else if ( product1 != null && product2 == null )
    {
        return 1;
    }

    IProductData productData1 = (IProductData ) product1.getProvidedProductData();
    IProductData productData2 = (IProductData ) product2.getProvidedProductData();

    if ( productData1.getVintage() == null && productData2.getVintage() == null )
    {
        return 0;
    }
    else if ( productData1.getVintage() == null && productData2.getVintage() != null )
    {
        return -1;
    }
    else if ( productData1.getVintage() != null && productData2.getVintage() == null )
    {
        return 1;
    }

    return productData2.getVintage().compareTo( productData2.getVintage() );
}

我对此不满意,因为我有很多重复的代码,而且我确信有更好的方法来做到这一点...任何建议都将不胜感激。

简单使用(我假设你在 Java8+ 因为你用 lambda 标记了问题) 来自 Comparator 的以下任一方法:

public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)

一种可能性是泛化 null 检查。

public int compare(IProduct product1, IProduct product2) throws ProductComparisonException {
    Integer diff = nullCompare(product1, product2);
    if(diff != null) return diff;

    IProductData productData1 = (IProductData)product1.getProvidedProductData();
    IProductData productData2 = (IProductData)product2.getProvidedProductData();

    diff = nullCompare(productData1.getVintage(), productData2.getVintage());
    if(diff != null) return diff;

    return productData2.getVintage().compareTo(productData2.getVintage());
}

// Using Integer so I can return `null`.
private Integer nullCompare(Object o1, Object o2) {
    if (o1 == null && o2 == null) {
        return 0;
    }
    else if (o1 == null && o2 != null) {
        return -1;
    }
    else if (o1 != null && o2 == null) {
        return 1;
    }
    return null;
}

您可以使用 Comparator 中介绍的方法 Java 8 并使用方法参考,例如:

Comparator.comparing(IProductData::getVintage, Comparator.nullsLast(Comparator.naturalOrder()))

将您的 Comparator 包裹在 Comparator.nullsFirst 中以避免处理可能 null 可用的参数。

您需要两个 ComparatorComparator#thenComparing 合并:

  1. nullsFirst(naturalOrder())先比较IProduct
  2. nullsFirst(comparing(p -> p...getVintage()) 再比较他们的 Vintage

    Comparator<IProduct> comparator =
        nullsFirst(Comparator.<IProduct>naturalOrder())
            .thenComparing(nullsFirst(
                comparing(p -> p.getProvidedProductData().getVintage())
            )
        );
    

这种方法比较 IProduct 很自然,您显然不想这样做。 (你根本没有比较它们)。

然后你可以写 IProduct p1, IProduct p2) -> 0 在两个 IProduct 都不是 null 之后继续比较 Vintages。

 Comparator<IProduct> comparator =
        nullsFirst((IProduct p1, IProduct p2) -> 0)
            .thenComparing(nullsFirst(
                 comparing(p -> p.getProvidedProductData().getVintage())
            )
        );

如果 getVintage returns 和 int,您可以使用 Comparator.comparingInt 而不是 Comparator.comparing:

comparingInt(p -> p.getProvidedProductData().getVintage())

这个怎么样?:

Comparator<IProduct> comparator = (p1, p2) -> {
    Integer v1 = p1 != null ? p1.getProvidedProductData() : null;
    Integer v2 = p2 != null ? p2.getProvidedProductData() : null;

    if (v1 == null ^ v2 == null)
        return v1 != null ? 1 : -1;

    return v1 != null ? v1.compareTo(v2) : 0;
};