HashSet中的唯一条目<list <t>&gt; list可能包含空条目</list <t>

时间:2012-01-23 13:03:03

标签: java null hashset

有一个List<MyElement> = new ArrayList<MyElement>();

class MyElement {
  private Object[] values;
  //...
}

我需要在此列表中找到所有唯一条目。我会使用HashSet,但问题是values可能包含null AND 应该假设null等于任何其他值。例如,Object[] o1 = new Object[]{1,null,"s2"}Object[] o2 = new Object[]{1,2,"s2"}应被视为相同的条目(即非唯一),其中只有一个应保留在HashSet中。有没有办法在HashSet中覆盖正确的函数?

2 个答案:

答案 0 :(得分:1)

你真的需要O(1)时间add()和contains()吗?我看不到为你的MyElement类编写一个满足你需求的hashCode()函数的好方法。

然而,比较器(或制作MyElement Comparable)可以解决问题,然后您可以使用TreeSet找出列表中的唯一元素。

这是第一次尝试(你不应该按原样使用它,它可能不起作用)。

class MyElementComparator implements Comparator<MyElement> {
    @Override
    public int compare(MyElement e, MyElement f) {
        int sizeCmp = e.values.length - f.values.length;

        if(sizeCmp != 0) // Lists are of different sizes, elements aren't equal
            return sizeCmp; 

        // Start comparing element by element
        for(int i=0; i<e.values.length; i++) {
            Object eo = e.values[i];
            Object fo = f.values[i];

            // Null is a wildcard
            if(eo == null || fo == null)
                continue;

            // If objects are the same, then continue too.
            if(eo == fo || eo.equals(fo))
                continue;

            // Otherwise, decide on one object or the other based on hashcode (or any other valid mean).
                return eo.hashCode() - fo.hashCode();
        }

        // All elements were equal or skipped, then the objects are equal.
        return 0;
    }
}

快速测试似乎表明它有效:

    MyElement a = new MyElement(1, null, "s2");
    MyElement b = new MyElement(1, 2, "s2");
    MyElement c = new MyElement(null, "s", 3);

    TreeSet<MyElement> set = new TreeSet<MyElement>(new MyElementComparator());
    set.add(a);
    set.add(b);
    set.add(c);
    System.out.println(set.size()); // 2

但如果你向set添加一个等于另外两个不同元素的元素,那么事情就会失败。例如{1}和{2}是不同的,但如果添加{null},则该集应该减少为{null},这不会发生。

没有比较者会实现这一点,你需要另一种数据结构,也许是一个不相交的集合(Union Find)? http://en.wikipedia.org/wiki/Disjoint-set_data_structure

答案 1 :(得分:1)

您的问题是空引用不应该等于任何内容,因为equals contract表示:

  

对于任何非空引用值x,x.equals(null)应返回false。

因此,如果您的values字段对您的equals实施有意义,那么您无法在不违反合同的情况下实施您所说的内容。

我会将Object[]字段替换为List字段,并在MyElement类中实现等号。这将为列表as its contract states提供有意义的等于。当然,如果你重写equals,你也应该覆盖hashcode以保持事物的一致性。

我要保持优秀的旧HashSet不受影响,请记住,写下正确的藏品不是一项微不足道的任务,无论乍一看多么容易。因此,覆盖您的MyElement哈希码并等于方法以满足您的需求而不会破坏这两个契约。