代码之家  ›  专栏  ›  技术社区  ›  pvgoddijn

空值比较器

  •  52
  • pvgoddijn  · 技术社区  · 14 年前

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

    但是,列表中有时会出现不带坐标的地址,从而导致nullpointerexception。我最初的想法是让比较器返回0作为至少一个坐标为空的地址的距离。我担心这可能会导致列表中“有效”元素的顺序损坏。

    因此,在比较器中为空数据返回“0”值是否正确,或者是否有更干净的方法来解决此问题。

    9 回复  |  直到 8 年前
        1
  •  73
  •   BuZZ-dEE Brian Goetz    8 年前

    像这样处理 null 意味着无限遥远。因此:

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

    有了这个,你就能得到一个一致的顺序。

        2
  •  22
  •   Cowan    14 年前

    只是为了进一步说明威利·施伯恩的答案,我来这里是想说 google-collections 正是你想要的。

    一般来说,你可以自己写 Comparator 忽略空值(假定为非空,这样它就可以专注于重要的逻辑),然后使用 Ordering 要处理空值:

    Collections.sort(addresses, Ordering.from(new AddressComparator()).nullsLast());
    

    不过,在你的例子中,用来排序的是地址(坐标)内的数据,对吧?谷歌收藏甚至 更多 在这种情况下很有用。所以你可能会有更像:

    // 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
    

    然后,当您要排序时:

    Collections.sort(addresses,
        Ordering.from(COORDINATE_SORTER)
                .nullsLast()
                .onResultOf(ADDRESS_TO_COORDINATES));
    

    这就是谷歌收藏的力量真正开始发挥作用的地方。

        3
  •  8
  •   Paul Croarkin    12 年前

    我的看法是你想做的任何事 null 坐标只是在裂缝上盖上了纸。你真正需要做的是找到并修复那些注入虚假代码的错误。 无效的 协调。

    根据我的经验,npe bug的感染通常是由以下不良的编码习惯引起的:

    • 输入参数验证不充分,
    • 使用 无效的 为了避免创建空数组或集合,
    • 返回 无效的 当应该抛出异常时,或
    • 使用 无效的 当有更好的解决方案时表示“无价值”。

    (更好的解决“无价值”问题的方法通常包括重写代码,这样您就不会 需要 表示此值和/或使用非空值;例如空字符串、特殊实例、保留值。你不可能总能找到更好的解决方案,但你通常可以。)

    如果这描述了您的应用程序,那么您应该花时间根除代码问题,而不是想办法隐藏npe。

        4
  •  8
  •   stoilkov    12 年前

    我的解决方案(可能对这里的人有用)是做比较正常,用空值代替不是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);
            }
    

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

        5
  •  3
  •   LenW    14 年前

    你可能不想返回0,因为这意味着地址是等距的,你真的不知道。这是一个非常典型的问题,您试图处理错误的输入数据。我不认为比较器的责任是试图确定地址的实际距离,当你 不知道 距离。我会在排序之前把这些地址从列表中删除。

    黑客会把它们移到列表的底部(但这很难看!)

        6
  •  2
  •   Adrian Cosma    8 年前

    如果使用Java 8,则在比较器类中有2个新的静态方法,这是很有用的:

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

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

    以下示例:

    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 + "] "));
    

    将打印: [空][空][[黑猩猩][香蕉][吃][跑][睡][笑][扔香蕉皮]

        7
  •  1
  •   Wouter Coekaerts    14 年前

    与其把这看作是比较器的技术问题,不如再看一下需求:您在这里真正想做的是什么,您打算如何处理这个排序列表?

    • 如果您试图对它们进行排序,以便首先向用户显示最相关的解决方案,那么最好将未知位置放在最后,因此将其视为无穷大(根据其中哪个位置为空,返回0/-1/1)。
    • 如果要使用这个结果来绘制一些图形或进行一些其他计算,这些计算依赖于按距离排序的结果,那么空值可能无论如何都不应该出现在其中(因此,要么先删除它们,要么抛出一个异常,如果此时实际上没有su假定为任何位置为空的地址)。

    正如您已经意识到的,当其中一个值为空时总是返回0在这里不是一个好主意;它确实会破坏结果。但是你应该做什么取决于你需要什么,而不是其他人通常做什么/需要什么。程序如何处理没有位置的地址(用户将看到什么)不应该取决于一些技术细节,比如比较器的“最佳实践”是什么。 (对我来说,问这里的“最佳实践”是什么,听起来就像问“最佳要求”是什么)。

        8
  •  1
  •   Bozho    14 年前

    不,没有更干净的方法。也许:

    • 如果两个比较对象的坐标都为空,则返回0
    • 如果其中一个对象的坐标为空,则返回-1/1(取决于它是第一个参数还是第二个参数)

    但更重要的是-试着去掉/填充缺少的坐标,或者,更好的做法是:不要在列表中添加缺少坐标的地址。

    实际上,不把它们放在列表中是最合乎逻辑的行为。如果你把它们放在列表中,结果实际上不会按距离排序。

    您可以创建另一个列表,其中包含缺少坐标的地址,并向需要该信息的人(最终用户、api用户)表明,第一个列表仅包含具有所需数据的地址,而第二个列表包含缺少所需信息的地址。

        9
  •  1
  •   whiskeysierra    14 年前

    我个人不喜欢在我的比较器中处理任何特殊的空情况,所以我一直在寻找一个更干净的解决方案,最终找到了谷歌的收藏。他们点的菜太棒了。它们支持复合比较器,提供从零到上到尾的排序,并允许在比较之前运行某些函数。编写比较器从来没有这么容易过。你应该试一试。