2011-05-13 62 views
9

我很好奇,从可变类返回对象的集合时,什么被认为是一个更好的做法:在Java中返回内部集合的最佳做法是什么?

public class Someclass { 

    public List<String> strings; 

    public add(String in){ strings.add(in); } 
    public remove(String in) { strings.remove(in); } 

    //THIS 
    public List<String> getStrings(){ 
    return Collections.unmodifiableList(strings); 
    } 

    //OR THIS 
    public List<String> getStrings(){ 
    return new ArrayList(strings); 
    } 
} 

我一直认为在包装不可修改的内部集合是最好的方法,因为我没看到这是造成创建副本开销的原因。然而,我发现,内部对列表所做的任何更改都会暴露给客户,这让我感到不好。

回答

5

我不认为这是一个简单的“最佳实践”答案。这实际上取决于您愿意花费多少机器资源来防止违反类数据抽象边界。这取决于你实际尝试缓解的风险;例如它是简单的(非并发)错误,并发错误还是信息泄漏。

的选项有:

  • 什么都不做;即按原样返回集合。
  • 返回包装在不可变包装类中的集合。
  • 返回集合的浅表副本。
  • 返回集合的深层副本。

除了复制的直接成本之外,其他与性能有关的因素还包括内存使用率和垃圾产生以及对并发性的影响。例如,如果多个线程正在更新集合和/或“获取”集合,那么创建集合的副本通常需要锁定集合......这可能会使操作成为并发瓶颈。

总之,您需要平衡对未采取预防措施的潜在或实际风险和成本的成本/性能影响。

1

我喜欢做后者,但使用.clone()方法。

+0

后一种方法使用新的ArrayList或使用.clone()是减少并发问题的正确方法。 Collections.unmodifiableList()增加了多线程环境中可能发生的并发问题。 – 2011-05-13 01:15:35

0

如果您担心暴露更改;并且您的对象实现了Cloneable,那么您可以将对象列表的克隆深层副本返回给调用者。

0

'new ArrayList(strings)'比'Collections.unmodifiableList(strings)'更安全。

因为Collections.unmodifiableList只是转发源列表。

样品:

List<String> list1 = someclass.getStrings(); //Use Collections.unmodifiableList(strings) implementation 
List<String> list2 = someclass.getStrings(); //Use new ArrayList(strings) implementation 

someclass.add("foo"); 

现在,你会看到列表1中加入! list2不是。

相关问题