How to return a thread safe/immutable Collection in Java?
Asked Answered
C

6

11

In the project I am coding, I need to return a thread safe and immutable view from a function. However, I am unsure of this. Since synchronizedList and unmodifiableList just return views of a list, I don't know if

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

would do the trick.

Could anyone tell me if this is correct, and in case it is not, are there any situations that this would likely to fail?

Thanks for any inputs!

Costumer answered 12/7, 2011 at 1:45 Comment(0)
R
13

I find this to be a real gap in the JDK. Fortunately, a team over a Google, led by Java Collections designer Joshua Bloch, have created a library that includes truly immutable collections.

ImmutableList in particular is the implementation you're looking for. Here is a quick sketch of some of the features of Guava's ImmutableCollections.

Ricer answered 12/7, 2011 at 1:48 Comment(5)
+1 Ray, can you link or elaborate by what you mean a real gap in JDK, is this up till JDK 6 and 7?Siderosis
I don't know of such an addition in JDK 7; I don't know of a truly immutable list in JDK 6.Ricer
You can get the same result though by making a copy of the original list and returning an unmodifiable view of the copy. So it basically only saves one line of code - but then if one is already using Guava why write one extra line of code?Immedicable
@Voo, yes and no. You're right that the unmodifiable view of a discarded copy would really be immutable. But, then, suppose you are the consumer of this list. You don't know if the producer has retained the modifiable list or not. So you have to make a defensive copy yourself, which has some overhead associated with it. If you were using a list could guarantee that it could not be modified by any means, the programmer could be confident that this step could be avoided. As a matter of fact, if you try to make a defensive copy of Guava's ImmutableList, it will return you the original instance.Ricer
It there a proof Guava Immutable Collection is REALLY thread-safe? I am still not sure. The question is not about concurrent changes, but about Java memory model concurrency effects.Peruzzi
V
5

I think unmodifiable is sufficient. You can't write to it, which is what causes problems for multi-threaded access. It's read-only, so the additional step of synchronizing seems unnecessary to me.

Best to check out the source code when there are questions like this. Looks like it returns an 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();
            }
    };
}
Virgule answered 12/7, 2011 at 1:47 Comment(3)
But isn't unmodifiable just return a view that the thread that get this view cannot modify the list through it? Or maybe I am interpreting it wrong?Costumer
The consumer would be unable to modify the list, but the original list could be modified if it is retained.Ricer
That is correct, as long as no-one modifies the "original" (unwrapped) collection. It's a good idea to limit this to collections you constructed yourself. Losing your own reference to the original once you wrap it with unmodifiable is also a good idea. Safer still is to use the immutable collections mentioned in Ray's answer, as there is no mutable collection to accidentally leak out.Prelatism
F
5
Collections.unmodifiableList(this.data) 

Statement above will do, as it will return a view. Any modification attempts on this view will result of UnsupportedOperationException being thrown. Below are excerpt of Collections#unmodifiableList documentation.

Returns an unmodifiable view of the specified list. This method allows modules to provide users with "read-only" access to internal lists. Query operations on the returned list "read through" to the specified list, and attempts to modify the returned list, whether direct or via its iterator, result in an UnsupportedOperationException.

......

java 8 java.util.Collections javadoc

Facilitate answered 2/3, 2016 at 8:7 Comment(0)
E
3

copyOf

Yes, now built into Java 10 and later.

Each of these returns a separate collection of the objects found in the original. The returned collection is not a view onto the original, as is the case with the Collections.unmodifiable… utility class methods.

The copyOf methods are copying the references (pointers), not the objects. So, not much memory is involved.

Efficacious answered 10/4, 2020 at 0:51 Comment(2)
Isn't it too costly in terms of memory usage?Dyad
@AmirHossain The copyOf methods are copying the references (pointers), not the objects. So, no, not much memory is involved.Efficacious
D
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

Dyad answered 4/6, 2021 at 19:44 Comment(0)
S
0

These views won't return you truly thread-safe collections. There is always the possibility that someone will modify either the backing collection, or the elements within the collection.

To solve this, you need to use immutable collections and immutable elements. Then, thread-safety happens as a result.

Clojure contains such immutable (or persistent) collections.

Put simply, adding or removing new elements returns a new collection, which in general does reuse large parts of the old collection through clever use of Trie-type data structures.

On their own, these are a poor fit for using in straight Java.

Pure4j is an attempt to port these (and the immutable/value based style advocated by Clojure) to the Java language. It might be what you're after.

Disclaimer: I am the developer of Pure4J

Saltire answered 6/11, 2015 at 14:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.