0

如果我想调用这样的方法:

  List f(List l){
      l.add(new Object());
      return l;
  }

一切都很好,除非我调用该方法,它实际上修改了它的参数,是否还有呢?

// suppose l is instantiated at this point
log.info(l.count());// prints 0
f(l);
log.info(l.count());// prints 1

无论如何要声明 f 以在 java 中保持 l 不变?

我知道我可以对 l 执行深度克隆并通过它,但是在 l 非常大的情况下,这个操作很昂贵。

4

5 回答 5

6

好吧,不要调用会修改它的方法。如果不进行复制,您希望这样的方法能做什么?它要么必须表现不同(例如,在被调用时不做任何事情add),要么抛出异常。您可以通过将其包装在不可修改的列表中来使其抛出异常......但如果该方法的目的是更改集合,您可能希望抛出异常......

我知道这听起来有点陈词滥调,但我希望它真正触及你需要考虑的核心:如果你有一个不应该修改的集合,并且你想调用一个方法来尝试要修改集合,您应该首先考虑为什么要调用该方法。

我确实知道困难的部分是知道哪些方法修改集合 - 这就是您可以防御性地创建不可修改的包装器的地方,或者确保正确记录所有相关方法。

于 2011-02-21T17:24:17.727 回答
2

如果您不想更改原始列表,请不要更改它。

您可以改为更改副本。

List f(List l){
  l = new ArrayList(l); // the original will not be changed now.
  l.add(new Object());
  return l;
}
于 2011-02-21T17:25:10.160 回答
2

使用不可修改的列表

log.info(l.count());
f(Collections.unmodifiableList(list));
log.info(l.count());

如果您尝试在方法中修改列表,您将获得一个UnsupportedOperationException.

于 2011-02-21T17:23:39.537 回答
0

这就是为什么您应该始终从编写规范开始,并注意提供给您的有关 API 的规范。这将在方法的规范中列出。

如果您想强制不对您的列表进行任何更改,不管规范说它将尝试这样做(假设您没有编写方法本身),将其包装为 anCollections.unmodifiableList(l);并处理抛出的异常,作为其他人建议。

如果您在另一边 - 编写方法并且您想确保不更改列表的内容 - 只需不要编写任何修改语句并确保在规范中提及。

于 2011-02-21T18:08:18.207 回答
0

如果您知道您的原始列表不会自行更改,并且想要一个包含原始列表的所有内容以及一个新元素的新列表,您可以考虑在两者周围使用包装器,如下所示:

/**
 * an immutable wrapper around a list with an added element at the end.
 */
class ImmutableListWrapper<E> extends AbstractList<E> {

    private final List<E> delegate;
    private final E lastElement;

    public ImmutableListWrapper(List<E> start, E last) {
       this.delegate = start;
       this.lastElement = last;
    }


    public E get(int index) {
       if(index == delegate.size()) {
           return lastElement;
       }
       return delegate.get(index);
    }

    public int size() {
        return delegate.size() + 1;
    }
}

public List<Object> f(List<Object> l) {
    return new ImmutableListWrapper<Object>(l, new Object());
}

如果原始列表发生变化,新列表也会发生变化,这是设计使然。

如果您的原始列表是非随机访问列表,那么您最好从 AbstractSequentialList 继承并实现委托 ListIterator 而不是 get 方法。

于 2011-02-22T00:35:45.597 回答