2017-02-28 45 views
-1

我有一个SetA类型的元素。一个集合应该只包含不可变元素吗?

如果我修改集合中包含的元素(考虑到这种修改可能会改变任何a的身份),我的代码是否有异味?

+6

它不只是闻。它打破了。 –

+1

取决于哪些字段用于计算hashCode/equals以及哪些字段被修改。 – kerner1000

回答

1

大多数时候一组应该只包含immutables。

the JavaDoc of the Set interface

注意:如果使用可变对象作为一组元素大,一定要小心。没有指定集合的行为如果对象的值以影响等于比较的方式更改,而对象是集合中的元素,则该对象的值会发生更改。这种禁令的一个特例是,一个集合不允许自己作为一个元素。

强调我的。

如果您能够在不影响equals()的情况下修改元素,我会强烈重新考虑您的等值实现是否正确。

+0

如果程序员编写调用它的代码并不会让任何程序员惊讶,那么'equals()'的实现就是_correct_。 (这并不意外,任何程序员都会在Set中使用你的类的成员。)如果你是唯一一个会使用你的类编写代码的人,那么你的意见是唯一重要的。另一方面,如果您打算让您的代码被其他人重新使用,那么您可能想要更加认真地考虑其他人期望“equals()”的含义。 –

+0

我不同意。我相信任何'equals'的实现都应该比较每个字段。这是将被普遍理解的* only *实现。如果你想比较一个对象字段的子集,你应该添加另一个明智的命名方法:'isSamePlaceAs()','isSameSizeAs()'等。 – Michael

1

由于您可以通过修改里面的可变元素来打破Set,所以不可变的肯定是首选

3

一个问题,举例来说,就是如果你添加一个A a一组,然后在改变其哈希码,有一个机会,set.contains(a)将返回错误的方式发生变异a

简单的例子:

public static void main(String[] args) { 
    Set<A> set = new HashSet<>(); 
    A a = new A(1); 
    set.add(a); 
    System.out.println(set.contains(a)); //true 
    a.i = 2; 
    System.out.println(set.contains(a)); //false 
} 

static class A { 
    int i; 
    public A(int i) { this.i = i; } 
    @Override public int hashCode() { return i; } 
} 
+0

一个假设你的例子假定一个相应的'equals'实现,即使你没有显示它。 –

+0

@LewBloch这个例子不需要工作,但是一般情况下你也可以重写'equals'。 – assylias

+0

如果'equals'方法返回错误的答案,那么'Set'如何找到该项目?是的,这个例子是必需的。请记住,哈希集合既使用'hashCode'也使用equals。如果它们不一致,代码就会中断。实际上,你声称破碎的代码是可以接受的。 –

0

它最有可能闻起来,因为如果一个字段被修改,也被用来计算hashCode,Set打破。因此可以打破Set

相反,修改List中的元素将永远不会破坏该列表。

简单的例子(截断并从assylias扩展回答)

public class Main { 

    public static void main(String[] args) { 
    Set<A> set = new HashSet<>(); 
    List<A> list = new ArrayList<>(); 
    A a = new A(1); 
    set.add(a); 
    list.add(a); 
    System.out.println(set.contains(a)); // true 
    System.out.println(list.contains(a)); // true 
    a.i = 2; 
    System.out.println(set.contains(a)); // false 
    System.out.println(list.contains(a)); // true 
    } 

    static class A { 
    int i; 

    public A(int i) { 
     this.i = i; 
    } 

    @Override 
    public int hashCode() { 
     return i; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (obj == null) { 
     return false; 
     } 
     if (!(obj instanceof A)) { 
     return false; 
     } 
     A other = (A) obj; 
     if (i != other.i) { 
     return false; 
     } 
     return true; 
    } 
    } 
} 
+0

假设你的例子假定你有相应的'equals'实现,即使你不要显示 –

+0

谢谢Lew,固定。 – kerner1000

+0

建议成语:'return i == other.i'。在'if'中使用'boolean'来确定'boolean'有点多余。 –

相关问题