从参数中分配集合的首选方法是什么?

时间:2010-03-03 08:26:13

标签: java

我有这堂课:

public MyClass {
    public void initialize(Collection<String> data) {
        this.data = data; // <-- Bad!
    }
    private Collection<String> data;
}

这显然是糟糕的风格,因为我引入了一个共享的可变状态。处理这个问题的首选方法是什么?

  • 忽略它?
  • 克隆收藏品?
  • ...?

编辑:为了澄清为什么这很糟糕,想象一下:

MyClass myObject = new MyClass();
List<String> data = new ArrayList<String>();
myObject.initialize(data); // myObject.data.size() == 0
data.add("Test"); // myObject.data.size() == 1 

只是存储引用就可以将数据注入私有字段myObject.data,尽管它应该是完全私有的。

根据MyClass的性质,这可能会产生严重影响。

5 个答案:

答案 0 :(得分:9)

最好的方法是深度克隆参数。出于性能原因,这通常是不可能的。最重要的是,并非所有对象都可以克隆,因此深度复制可能会抛出异常并导致各种头痛。

下一个最好的方法是“写时复制”克隆。在Java运行时中没有对此的支持。

如果您认为某人可能会改变该集合,请使用复制构造函数执行浅表复制:

this.data = new HashSet<String> (data);

这将解决您的问题(因为String是不可变的)但是当集合中的类型是可变的时它会失败。

另一个解决方案是,只要将它们存储在某处,就始终使这些集成为不可变的:

Set<String> set = ...
...build the set...

// Freeze the set
set = Collections.unmodifiableSet(set);

// Now you can safely pass it elsewhere
obj.setData (set);

这里的想法是尽快将集合变成“价值对象”。任何想要更改集合的人都必须复制它,更改它然后再保存。

在一个类中,你可以保持set的可变性并将它包装在getter中(你应该这样做)。

这种方法存在的问题:性能(但可能没有你期望的那么糟糕)和纪律(如果你忘了它就会​​中断)。

答案 1 :(得分:3)

  • 空检查(如果要限制空值)
  • 防御性副本(如果你不想要共享状态)
  • 或正如您所做的那样(如果数据的实时视图很有用)

在很大程度上取决于您的要求。

编辑: 忽略应该是没有选择。沉默的失败是......调试的噩梦。

答案 2 :(得分:1)

public class Foo {
    private final Collection collection = new ArrayList();
    public void initialise(final Collection collection) {
        this.collection.addAll(collection);
    }
}

答案 3 :(得分:1)

很抱歉没有直接解决您的问题,但我永远不会直接将Collection传递给setXxx()bean setter方法。相反,我会这样做:

private final List<MyClass> theList;

public void addXxx(MyClass item) { ... }
public void removeXxx(MyClass item) { ... }  // or index.

public void Iterator<MyClass> iterateXxx() {
  return Collections.unmodifiableList(theList).iterator();
}

只有当我确定使用它没有副作用时我会选择防御性复制/深度克隆,至于速度,我不会关心它,因为在商业应用中可靠性有10倍比速度更重要。 ; - )

答案 4 :(得分:0)

一个想法是将数据作为String数组传递并在MyClass中创建Set。当然,MyClass应该测试输入数据是否有效。无论如何,我相信这是一个很好的做法。

如果MyClass和MyClass的调用者本身都使用Set<String>,那么您可以考虑克隆该集合。然而,集合需要以某种方式构建。我更愿意将此责任移交给MyClass。