禁用 Java 列表中的可变性



不再需要突变的时间点将 Java List 接口的对象转换为不可变的等价物会很好。 也就是说,客户端可以调用使 List 不可变的 freeze 方法。 对我来说,直接的好处是线程安全,没有

深度复制的内存开销。 (编辑:如果人们假设一个额外的不可变副本,供所有线程使用,是负担得起的,那么他们是正确的。

是否有提供此类功能的第三方接口或类?

Collections.unmodifiableList(List list)怎么样?

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

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

尝试使用 CopyOnWriteArrayList

CopyOnWriteArrayList 的行为与 ArrayList 类非常相似,不同之处在于当列表修改后,将创建一个新数组并丢弃旧数组,而不是修改基础数组。这意味着当调用者获得一个迭代器(即copyOnWriteArrayListRef.iterator() ),它在内部包含对基础 CopyOnWriteArrayList 对象的数组的引用,该数组是不可变的,因此可以是用于遍历,不需要在列表 copyOnWriteArrayListRef 上进行同步,也不需要clone() 遍历前的 copyOnWriteArrayListRef 列表(即没有并发修改的风险)和还提供更好的性能。

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

我将创建一个委托列表实现,该实现将具有对原始可变列表(委托)的内部引用,并将所有方法调用转发到它。手动编写此类代码是一个PITA,但是例如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.
}

相关内容

  • 没有找到相关文章

最新更新