实现可比较的使用对象哈希码

时间:2017-10-25 15:02:35

标签: java

我有一个需要排序和搜索通用对象的通用方法,

旧版

    public <T> int isIn(T[] list, T t) {

       Arrays.sort(list, Comparator.comparingInt(Object::hashCode));  
 return Arrays.binarySearch(values,updatedObject.hashCode())

    }

新版本:

    public <T> int isIn(T[] list, T t) {

       Arrays.sort(list, Comparator.comparingInt(Object::hashCode));  
 return Arrays.binarySearch(values,updatedObject.hashCode(),Comparator.comparingInt(Object::hashCode))

    }

假设hashcode()已正确实现,我无法想到任何可能失败或出现任何错误的情况。 有什么情况,如果有的话,这可能会给我们一个错误!

注意:代码已编辑,我将Comparator添加到二进制搜索

4 个答案:

答案 0 :(得分:3)

您的代码存在很多问题。

  1. hashCode用于推断对象不等式。这意味着两个对象实际上可能具有相同的hashCode,而equals调用可能会返回false(另请参阅here)。在这种情况下,binarySearch的结果可能会产生意外结果(另请参阅here)。
  2. binarySearch调用而不传递Comparator假设您的对象根据其自然顺序进行排序,这意味着它们是Comparable(我们无法通过您的代码知道)。如果您的对象不是Comparable,则在调用ClassCastException时会得到binarySearch。在排序数组时,您至少需要使用Comparator,并将其传递给binarySerach调用(请参阅here了解重载API)。
  3. 注意:OP添加了Comparator并编辑了问题 - 仅对问题的先前版本有效。

    1. 作为一般的OO指南,您可能想要重新考虑将要使用isIn方法的对象,并且可能将泛型类型绑定到广泛类型,从而产生足够的属性信息一个自然的顺序,或者至少可以使用Comparator基于有意义排序的属性进行排序。 TL; DR哈希码的目的不是对对象进行排序。
    2. 关注第3点,您希望isIn方法返回布尔类型,而不是整数类型。如果您的目标只是推断您的数组是否包含给定值,请在传递的对象中覆盖equals,将数组包装在合适的Collection中并调用contains

答案 1 :(得分:1)

您的代码将生成具有相同哈希码的对象的索引,但由于允许对象具有相同的哈希代码而不相等,因此您需要在返回结果之前执行一些额外的工作。

使用相同的哈希码遍历项目列表,直到找到相等的内容,找到不同的哈希代码,或者离开数组的末尾:

public <T> int isIn(T[] list, T t) {
    Comparator<T> cmp = Comparator.comparingInt(Object::hashCode);
    Arrays.sort(list, cmp);
    int pos = Arrays.binarySearch(list, t, cmp);
    if (pos < 0) {
        return -1;
    }
    // At this point pos is a valid index, but it may not be of the same object
    // Continue with a linear search of the equal range of hash codes from here:
    int hash = t.hashCode();
    while (pos != list.length()) {
        if (t.equals(list[pos]) {
            return pos;
        }
        if (list[pos].hashCode() != hash) {
            return -1;
        }
        pos++;
    }
    return -1;
}

注意:虽然这与Java中应该使用hashCode / equals的方式一致,但这种方法的效率低于使用{{1因为它需要O(n * log n)排序步骤。

答案 2 :(得分:1)

这应该失败;因为,当两个对象包含相同的哈希码时,将不指定顺序。哈希码的合同是所有返回equals(...)为真的对象具有相同的哈希码,并非所有相等的哈希码都有equals(...)返回true。

Hashcodes不是唯一的,这就是为什么我们将哈希码作为确定廉价平等的第一种方法,但总是必须在事后跟进equals(...)方法。

现在,equals(...)方法无法提供比较(排序,因此您无论如何都必须使用Comparator备份该对象。因为您最初订购了哈希码,您的Comparator必须提供不违反代号优先方法的订单。

  // pseudocode
  unless hashcodes are equal, return the value of Comparator.comparingInt()
  when hashcodes are equal, pick some stable ordering.

请注意,这可能会在代码的泡沫中发挥作用;但是,如果你想确保某人未来不会破坏它,你可能还需要为你的对象添加一个Comparable接口,并让它返回&#34; hashcode,然后是sub -ordering&#34;与您的Comparator具有相同的逻辑。

答案 3 :(得分:1)

  

假设hashcode()已正确实现

这是一个很大的假设,因为Java的库类(例如run_tests)本身在from manager import Manager import asyncio async def run_new_vm(manager, loop, vm_name): new_instance = manager.launch_ec2_instance(vm_name) task = loop.create_task(new_instance) task.add_done_callback(lambda f: do_something(manager, f.result())) def do_something(manager, instance): // Do stuff once the instance is usable async def one_service_per_vm(n, manager, loop): for x in range (0, n): print('Started with number %s.' % x) loop.create_task(run_new_vm(manager, loop, n)) def run_tests(): loop = asyncio.get_event_loop() m = Manager() loop.run_until_complete(one_service_per_vm(2, m, loop)) loop.close() 值中有冲突,例如

String

这将打印相同的哈希码,因此,比较器将认为两个对象相等。