8

在我正在编码的项目中,我需要从函数返回一个线程安全且不可变的视图。但是,我不确定这一点。因为synchronizedList并且unmodifiableList只是返回列表的视图,我不知道是否

Collections.synchronizedList(Collections.unmodifiableList(this.data));

会成功的。

谁能告诉我这是否正确,如果不是,是否有任何情况可能会失败?

感谢您的任何投入!

4

6 回答 6

13

我发现这是 JDK 中的一个真正差距。幸运的是,由 Java 集合设计师Joshua Bloch领导的 Google 团队创建了一个包含真正不可变集合的库。

ImmutableList特别是您正在寻找的实现。是 Guava 的 ImmutableCollections 的一些功能的简要概述。

于 2011-07-12T01:48:52.913 回答
5

我认为不可修改就足够了。您不能写入它,这就是导致多线程访问出现问题的原因。它是只读的,所以同步的额外步骤对我来说似乎是不必要的。

有此类问题时最好查看源代码。看起来它返回一个UnmodifiableList

/**
 * @serial include
 */
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                  implements List<E> {
    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<E> listIterator()   {return listIterator(0);}

public ListIterator<E> listIterator(final int index) {
    return new ListIterator<E>() {
    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();
            }
    };
}
于 2011-07-12T01:47:42.367 回答
5
Collections.unmodifiableList(this.data) 

上面的语句会做,因为它会返回一个视图。对此视图的任何修改尝试都将UnsupportedOperationException被抛出。以下是Collections#unmodifiableList文档的摘录。

返回指定列表的不可修改视图。此方法允许模块为用户提供对内部列表的“只读”访问权限。对返回列表的查询操作“通读”到指定列表,并尝试修改返回的列表,无论是直接还是通过其迭代器,都会导致 UnsupportedOperationException。

……

java 8 java.util.Collections javadoc

于 2016-03-02T08:07:33.867 回答
3
于 2020-04-10T00:51:51.197 回答
1

Java 9+ ImmutableCollections are thread safe. For example, List.of, Map.of, Map.copyOf(Java 10+)... According to oracle doc,

One advantage of an immutable collection is that it is automatically thread safe. After you create a collection, you can hand it to multiple threads, and they will all see a consistent view.

Read more at: oracle docs

于 2021-06-04T19:44:47.903 回答
0

这些视图不会返回真正的线程安全集合。总有可能有人会修改支持集合或集合中的元素。

要解决这个问题,您需要使用不可变集合和不可变元素。然后,线程安全就会发生。

Clojure 包含这样的不可变(或持久)集合

简而言之,添加或删除新元素会返回一个新集合,它通常通过巧妙地使用 Trie 类型的数据结构重用旧集合的大部分。

就其本身而言,它们不适合在纯 Java 中使用。

Pure4j尝试将这些(以及 Clojure 提倡的基于不可变/基于值的风格)移植到 Java 语言。这可能是你所追求的。

免责声明:我是 Pure4J 的开发者

于 2015-11-06T14:06:09.660 回答