Java:获取对象的唯一属性(如hashcode,但是防碰撞)

时间:2009-12-03 22:46:35

标签: java hashcode multiset

我有一项任务,必须为集合中的每个对象生成唯一值。如果哈希码合约中不允许冲突,则使用哈希码将是完美的。

一个想法:将每个对象的哈希码记录到多集中。然后,使用哈希码作为唯一标识符,但如果该哈希码多次出现在集合中,请使用不在集合中的其他值。但这感觉笨重而且很尴尬。

更好的想法?

这是我已经拥有的:

public static <V> void toGraphViz(final Graph<V, DefaultWeightedEdge> g, String filename) {

    // to avoid hashcode collisions
    final Set<Integer> hashcodes = new HashSet<Integer>(g.vertexSet().size());

    DOTExporter<V, DefaultWeightedEdge> dot = new DOTExporter<V, DefaultWeightedEdge>(new VertexNameProvider<V> () {

    // vertex name must be unqiue
    @Override
    public String getVertexName(V arg0) {
        int hash = arg0.hashCode();
        while (hashcodes.contains((hash))) {
            hash += 1;
        }
        return "" + hash;
    }
}

编辑:我想这本来不是很清楚,但是id号确实需要成为对象的函数,因为getVertexName(V)会多次被调用,而它我们希望对于相同的V值,它会得到相同的结果。

此外,Vertex类型是通用的。因此,我无法对特定类进行任何修改来解决此问题。

7 个答案:

答案 0 :(得分:4)

这个唯一号码的生命周期是多少?只是程序的生命周期?在这种情况下,为什么不只是一个简单的静态计数器在类中,通过适当的同步访问?为每个新对象增加它。无需保留已使用的值列表,只需保留您使用的最高值。

如果在许多执行中都是唯一的(也许是许多同时发生的实例),那么也许您可以使用生成unqiue记录ID的数据库。

为回应澄清而编辑

我之前错过的部分是我们无法修改我们想要生成唯一“哈希”的类。

我认为从类的哈希码开始工作会产生冲突,这会让生活变得艰难。假设我们可以依赖正确实现equals()的Vertex类,那么我们可以使用对象本身作为我们使用的哈希码集的关键。

public class Hasher {

    public  <V> void toGraphViz(final Graph<V, DefaultWeightedEdge> g, String filename) {
         final Map<V, Integer> hashcodes = new HashMap< V, Integer>();
         final int latestHashHolder[] = { 0 }; // array to allow access from inner class

         DOTExporter<V, DefaultWeightedEdge> dot 
                 = new DOTExporter<V, DefaultWeightedEdge>(new VertexNameProvider<V> ()) {

         // vertex name must be unqiue
            @Override
            public synchronized String getVertexName(V vertex) {
                int hashcode;
                if ( hashcodes.containsKey(vertex)){
                    hashcode = hashcodes.get(vertex);
                } else {                
                    hashcode = latestHashHolder[0];
                    latestHashHolder[0]++;
                    hashcodes.put(vertex, (Integer)latestHashHolder[0]);
                }
                return "Vertex-" + hashcode;
            }
        };
    }
}

答案 1 :(得分:2)

为什么不使用序列号?

static private int serial=0;
static public synchronized nextSerialNumber() { return ++serial; }

或者组合/混合,比较长((hash&lt;&lt; 32)| getNextSerial())。

解决EDIT澄清问题

构造对象时,将序列号分配给私有成员变量并将其返回给hashCode()。然后你应该通过调用super.equals()来覆盖equals(因为生成的序列号与默认的equals()实现一致)因为看到一个没有相应的equals()重写的hashCode()重写会使代码重新标记工具(和其他程序员)。

public class Vertex
{
private final int                   serial;                                 // instance serial number

public Vertex() {
    serial=nextSerialNumber();
    ...
    }

public int hashCode() {
    return serial;
    }

public boolean equals(Object obj) {
    return super.equals(obj);                                               // serial number hash-code consistent with default equals    
    }

...        

static private int nextSerial=0;
static public synchronized nextSerialNumber() { return nextSerial++; }
}

答案 2 :(得分:2)

您可以考虑使用UUID,具体取决于您要完成的工作......

答案 3 :(得分:2)

要查找对象的唯一值,您必须知道使对象唯一的属性组合。

要运行“.contains()”,你需要有一个确定“.equals()”的方法,这意味着你应该已经知道如何唯一地识别一个顶点,所以也许你可以想出一个表达式独特的属性?

例如,“(x,y,z,rgb)”

除非我误解了这个问题,否则我不建议为此目的使用对象的hashCode。

答案 4 :(得分:1)

我认为你误解了哈希码。 根据合同,当equals(..)为真时,hascode应该是相同的,反之亦然。所以在你的情况下,只有具有相同属性的顶点应该具有相同的hascode,否则你自己编写的hascode计算方法应该是固定的。据我所知,你的问题顶点本身是独特的,所以你不应该有问题,对吗?

答案 5 :(得分:0)

我可能不明白你在做什么,但考虑创建一个参考 到每个对象。由于引用包含它将成为的对象的地址 每个对象都是唯一的。

答案 6 :(得分:0)

这不是那么难,是吗?只是使用不同的哈希算法,如果Java中的哈希算法不保证没有冲突。将对象发送到散列算法,例如Sha-256,并用它作为关键。如果您需要使用不同的哈希值保留完全相同的对象的不同副本,请在执行哈希时使用种子,并使用哈希将其与对象相关联。