如何在Java中返回线程安全/不可变的集合?
在我编写的项目中,我需要从函数返回一个线程安全且不可变的视图。 但是,我不确定这一点。 由于synchronizedList
和unmodifiableList
只返回列表的视图,我不知道是否
Collections.synchronizedList(Collections.unmodifiableList(this.data));
会做的伎俩。
任何人都可以告诉我这是否正确,如果不是,有什么情况可能会失败吗?
感谢您的任何投入!
我发现这是JDK中的一个真正的差距。 幸运的是,由Java Collections设计师Joshua Bloch领导的Google团队创建了一个包含真正不可变集合的库 。
ImmutableList尤其是您正在寻找的实现。 这是Guava的ImmutableCollections的一些function的快速草图。
我认为不可修改就足够了。 你不能写它,这是导致multithreading访问问题的原因。 它是只读的,所以我不需要额外的同步步骤。
如果有这样的问题,最好查看源代码。 看起来它返回一个UnmodifiableList
:
/** * @serial include */ static class UnmodifiableList extends UnmodifiableCollection implements List { static final long serialVersionUID = -283967356065247728L; final List extends E> list; UnmodifiableList(List extends E> list) { super(list); this.list = list; } public boolean equals(Object o) {return o == this || list.equals(o);} public int hashCode() {return list.hashCode();} public E get(int index) {return list.get(index);} public E set(int index, E element) { throw new UnsupportedOperationException(); } public void add(int index, E element) { throw new UnsupportedOperationException(); } public E remove(int index) { throw new UnsupportedOperationException(); } public int indexOf(Object o) {return list.indexOf(o);} public int lastIndexOf(Object o) {return list.lastIndexOf(o);} public boolean addAll(int index, Collection extends E> c) { throw new UnsupportedOperationException(); } public ListIterator listIterator() {return listIterator(0);} public ListIterator listIterator(final int index) { return new ListIterator () { ListIterator extends E> i = list.listIterator(index); public boolean hasNext() {return i.hasNext();} public E next() {return i.next();} public boolean hasPrevious() {return i.hasPrevious();} public E previous() {return i.previous();} public int nextIndex() {return i.nextIndex();} public int previousIndex() {return i.previousIndex();} public void remove() { throw new UnsupportedOperationException(); } public void set(E e) { throw new UnsupportedOperationException(); } public void add(E e) { throw new UnsupportedOperationException(); } }; }
Collections.unmodifiableList(this.data)
会这样做,因为它会返回一个视图。 对此视图的任何修改尝试都将导致抛出UnsupportedOperationException
。 以下是Collections#unmodifiableList
文档的摘录。
返回指定列表的不可修改视图。 此方法允许模块为用户提供对内部列表的“只读”访问。 对返回列表的查询操作“读取”到指定列表,并尝试修改返回的列表,无论是直接还是通过其迭代器,都会导致UnsupportedOperationException。
……
java 8 java.util.Collections javadoc
这些视图不会返回真正的线程安全集合。 总有可能有人会修改后备集合或集合中的元素。
要解决此问题,您需要使用不可变集合和不可变元素。 然后,线程安全就会发生。
Clojure包含这样的不可变(或持久) 集合 。
简而言之,添加或删除新元素会返回一个新集合,通常可以通过巧妙使用Trie类型的数据结构来重用旧集合的大部分内容。
就其本身而言,这些不适合直接使用Java。
Pure4j试图将这些(以及Clojure提倡的基于不可变/值的风格)移植到Java语言中。 它可能就是你所追求的。
免责声明:我是Pure4J的开发人员