创建集合的副本

Creating a copy of a collection

我正在阅读 J. Bloch 的有效 Java,现在我在关于避免 returning nulls,但 returning 空集合的部分.这是该部分的代码示例:

// The right way to return a copy of a collection
public List<Cheese> getCheeseList() {
  if (cheesesInStock.isEmpty())
    return Collections.emptyList(); // Always returns same list
  else
    return new ArrayList<Cheese>(cheesesInStock);
}

我真的无法理解如果 cheesesInStock.isEmpty() 只 returning cheesesInStock 有什么问题。为什么 return 预定义的 Collections.emptyList() 更好。如果我们 return cheesesInStock 相反,我们可能会遇到什么样的麻烦。

如果方法 returns cheesesInStock - 调用者可能会向列表中添加一些奶酪。

这是一种不好的做法,因为您可能想要控制添加过程。

因为

  • 您将使用具有单例模式的 Collections.emptyList(); 节省资源。
  • 它是类型安全的
  • 列表是不可变的(无法修改)

还可以 look at the API 你可以找到:

Returns the empty list (immutable). This list is serializable.

This example illustrates the type-safe way to obtain an empty list:

 List<String> s = Collections.emptyList();

Implementation note: Implementations of this method need not create a separate List object for each call. Using this method is likely to have comparable cost to using the like-named field. (Unlike this method, the field does not provide type safety.)

这里的想法更多是关于安全对象发布或共享,返回对可变列表的引用允许调用者改变原始列表,这通常是一个坏主意。如果列表或一般对象是不可变的,那么您不需要这样做,例如 String 是不可变的,因此共享它是安全的。

String getString(){
  return someString; // no need to copy
}

共享可变状态有两个主要问题:

  1. 当事情可以从任何地方发生变异时,很难检查程序的正确性。
  2. 线程安全变得更加困难,因为它需要同步,这通常是困难且昂贵的。

如果您直接 return cheesesInStock,您 return 引用了与您所反对的相同的 List(而不是它);因此,获取此列表的人所做的任何更改都将反映在对象的内部表示中。示例:

List<Cheese> list = myObject.getCheeseList();
list.add(new Cheese()); // this also affects the list inside myObject

为防止出现这种情况,最好使用 return 列表副本而不是 new ArrayList<Cheese>(previousList)。请注意,除了 return 创建新的 List,您还可以 return 使用 Collections.unmodifiableList 的不可修改的 List 视图:这具有相同的目标 -防止调用代码修改对象的内部表示。

returning Collections.emptyList(); 而不是 returning new ArrayList<Cheese>(emptyList) 的优点是可以避免创建另一个对象。此外,Collections.emptyList(); 表示不可变的 List.

cheesesInStock 列表可以在以后进行结构修改,但是 Collections.emptyList() returns 是一个空列表,以后不能进行结构修改。Collections.emptyList() returns EmptyList ,一些函数是:-

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}
public E get(int index) {
    throw new IndexOutOfBoundsException("Index: "+index);
}