禁用Java List中的可变性

5
在不需要修改对象的情况下,将Java List接口的对象转换为不可变等价物,这样做会很好。也就是说,客户端可以调用一个“freeze”方法,使List不可变。对我来说,即时的好处是线程安全,而不需要进行深度复制的内存开销。(编辑:如果人们认为可以承受额外的不可变副本,以供所有线程使用,那么他们是正确的。)是否有第三方接口或类提供此功能?

1
你是指这些之一吗?http://download.oracle.com/javase/6/docs/api/java/util/Collections.html?is-external=true#unmodifiableList(java.util.List)。你可以构建自己的列表(作为一种策略),当调用freeze方法时,你可以将内部列表更改为不可变列表。 - Augusto
4个回答

6

3

Guava库中有一个ImmutableList类。您可以使用 copyOf 方法从现有的 Iterable 创建一个 ImmutableList ,例如。

List<String> immutableList = ImmutableList.copyOf(list);


1

如果客户端仍然引用原始可变列表,则直接使用Collections.unmodifiableList是不够的。

我会创建一个委托列表实现,它将具有对原始可变列表(代理)的内部引用,并将所有方法调用转发给它。手动编写这样的代码很麻烦,但例如Eclipse可以自动生成它。

然后,在调用freeze方法时,我会使用Collections.unmodifiableList包装原始列表,以确保将来对FreezingList的所有方法调用都通过不可修改的视图仅转到原始代理。

为了使事情更安全,但更不灵活,您可以更改以下构造函数,而不是将原始列表传递给它(这仍然可能使客户端引用原始可变列表),在内部实例化列表(例如作为ArrayList)。

public class FreezingList<E> implements List<E> {

    // the original list you delegate to (the delegate)
    private List<E> list;

    private boolean frozen = false;

    public FreezingList(List<E> list) {
        this.list = list;
    }

    public void freeze() {
        if (!frozen) {
            list = Collections.unmodifiableList(list);
            frozen = true;
        }
    }

    // all the delegating methods follow:
    public int size() {
        return list.size();
    }

    public E get(int index) {
        return list.get(index);
    }
    // etc. etc.
}

1

尝试使用CopyOnWriteArrayList

CopyOnWriteArrayList的行为类似于ArrayList类,但在修改列表时,它不会修改底层数组,而是创建一个新数组并且旧数组被丢弃。这意味着当调用者获取迭代器(即copyOnWriteArrayListRef.iterator()),它内部持有对底层CopyOnWriteArrayList对象数组的引用,该数组是不可变的,因此可以进行遍历,无需在列表copyOnWriteArrayListRef上同步或在遍历之前需要clone() copyOnWriteArrayListRef列表(即没有并发修改的风险),并且还提供更好的性能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接