具有空值的比较器

pvg*_*ijn 55 java comparator

我们有一些代码,它们根据坐标之间的距离对地址列表进行排序.这是通过collections.sort与自定义比较器完成的.

但是,有时会在列表中出现没有坐标的地址,从而导致出现NullPointerException.我最初的想法是让比较器返回0作为地址的距离,其中至少有一个坐标为空.我担心这可能会导致列表中"有效"元素的订单损坏.

所以在比较器ok中返回空数据的'0'值,或者有更清晰的方法来解决这个问题.

Sjo*_*erd 77

处理它就像null无限远的手段.从而:

  • comp(1234, null) == -1
  • comp(null, null) == 0
  • comp(null, 1234) == 1

通过这种方式,您可以获得一致的订购.

  • @izb,违反了比较反射性要求. (28认同)
  • 只是喊出来,以防万一我误解了评论的方式:@KevinBourrillion正在回应第一条评论,而不是@Sjoerd的选择答案 - @ Sjoerd的回答是正确的反思.(`comp(x,x)== 0`表示所有`x`) (6认同)
  • ...或comp(null,null)== - 1,取决于您是否希望两个无地址条目显示为位于同一位置. (2认同)

Cow*_*wan 24

为了扩展WilliSchönborn的答案,我来到这里说google-collections正是你在这里所追求的.

在一般情况下,您可以编写自己的方法Comparator来忽略空值(假设非null,因此它可以专注于重要的逻辑),然后使用Ordering来处理空值:

Collections.sort(addresses, Ordering.from(new AddressComparator()).nullsLast());
Run Code Online (Sandbox Code Playgroud)

但是,在您的情况下,它是用于排序的地址(坐标)中的数据,对吧?在这种情况下,google-collections 更有用.所以你可能会有类似的东西:

// Seems verbose at first glance, but you'll probably find yourself reusing 
// this a lot and it will pay off quickly.
private static final Function<Address, Coordinates> ADDRESS_TO_COORDINATES = 
  new Function<Address, Coordinates>() {
      public Coordinates apply(Address in) {
          return in.getCoordinates();
      }
  };

private static final Comparator<Coordinates> COORDINATE_SORTER = .... // existing
Run Code Online (Sandbox Code Playgroud)

然后当你想要排序:

Collections.sort(addresses,
    Ordering.from(COORDINATE_SORTER)
            .nullsLast()
            .onResultOf(ADDRESS_TO_COORDINATES));
Run Code Online (Sandbox Code Playgroud)

这就是谷歌收藏的力量真正开始得到回报的地方.

  • 请注意,您可以首先使COORDINATE_SORTED扩展Ordering并跳过Ordering.from(). (3认同)
  • 你甚至可以使用另一个.nullsLast()与`Ordering.from(COORDINATE_SORTER).nullsLast().onResultOf(ADDRESS_TO_COORDINATES).nullsLast()`来允许列表中的空地址.对这个问题可能没有意义,但总的来说可能有意义. (2认同)

Ste*_*n C 8

我对此的看法是,你试图做的任何事情都是为了"改善" null坐标,只是在克服裂缝.你真正需要做的是找到并修复注入虚假null坐标的错误.

根据我的经验,NPE错误的侵扰通常是由以下不良编码习惯引起的:

  • 输入参数验证不充分,
  • 使用null,以避免产生空阵列或集合,
  • null在应该抛出异常时返回,或者
  • 使用null时,有一个更好的解决方案来表示"没有价值".

(对"无价值"问题的更好解决方案通常涉及重写代码,以便您不需要代表此代码和/或使用非空值;例如,空字符串,特殊实例,保留值.您可以总能找到更好的解决方案,但你经常可以.)

如果这描述了您的应用程序,您应该花时间根除代码问题,而不是考虑隐藏NPE的方法.

  • @SteveKuo - 在这种情况下,你最好用"Coordinate"实例替换它,这意味着"未知坐标". (3认同)

sto*_*kov 8

我的解决方案(对于看这里的人可能有用)是做比较法线,空值不是由0替换,而是可能的最大值(例如Integer.MAX_VALUE).如果您的值本身为0,则返回0不一致.这是一个正确的示例:

        public int compare(YourObject lhs, YourObject rhs) {
            Integer l = Integer.MAX_VALUE;
            Integer r = Integer.MAX_VALUE;
            if (lhs != null) {
                l = lhs.giveMeSomeMeasure();
            }
            if (rhs != null) {
                r = rhs.giveMeSomeMeasure();
            }
            return l.compareTo(r);
        }
Run Code Online (Sandbox Code Playgroud)

我只是想补充一点,你不需要整数的最大值.这取决于你的giveMeSomeMeasure()方法可以返回什么.例如,如果您比较天气的摄氏度,则可以将l和r设置为-300或+300,具体取决于您要设置空对象的位置 - 指向列表的头部或尾部.


Adr*_*sma 5

如果您使用的是Java 8,则Comparator类中有2个新的静态方法,它们很方便:

public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)
Run Code Online (Sandbox Code Playgroud)

比较将是null安全的,您可以选择将null值放置在排序序列中的位置。

下面的例子:

List<String> monkeyBusiness = Arrays.asList("Chimp", "eat", "sleep", "", null, "banana",
            "throw banana peel", null, "smile", "run");
Comparator<? super String> comparator = (a, b) -> a.compareTo(b);
monkeyBusiness.stream().sorted(Comparator.nullsFirst(comparator))
            .forEach(x -> System.out.print("[" + x + "] "));
Run Code Online (Sandbox Code Playgroud)

会打印:[null] [null] [] [Chimp] [banana] [eat] [run] [sleep] [smile] [throw banana skin]