10

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!

zw324
  • 26,764
  • 16
  • 85
  • 118

6 Answers6

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.

Ray
  • 4,829
  • 4
  • 28
  • 55
  • +1 Ray, can you link or elaborate by what you mean a real gap in JDK, is this up till JDK 6 and 7? – Oh Chin Boon Jul 12 '11 at 01:52
  • I don't know of such an addition in JDK 7; I don't know of a truly immutable list in JDK 6. – Ray Jul 12 '11 at 02:09
  • 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? – Voo Jul 12 '11 at 03:59
  • 1
    @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. – Ray Jul 12 '11 at 13:42
  • 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. – 30thh Mar 09 '18 at 18:05
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

Awan Biru
  • 373
  • 2
  • 10
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();
            }
    };
}
duffymo
  • 305,152
  • 44
  • 369
  • 561
  • 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? – zw324 Jul 12 '11 at 01:49
  • 3
    The consumer would be unable to modify the list, but the original list could be modified if it is retained. – Ray Jul 12 '11 at 01:53
  • 1
    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. – Laurence Gonsalves Jul 12 '11 at 01:57
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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
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

Amir Hossain
  • 184
  • 11
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

Rob Moffat
  • 465
  • 5
  • 11