1

The java util Collections class offers to create an "unmodifiable" decorator around any existing list. But as we all know (or learn the hard way at some point); it is really just a decorator around the list that was originally passed into that call. The decorated list can't be changed; but it will change under the covers if the "original" list is modified.

Now assume that I have some class like

class Whatever {
   private final List<IDontCare> someElements;
   public Whatever(List<IDontCare> incomingElements) {
      someElements = unmodifiableCopyOf(incomingElements);

That class simple wants to use a really unmodifiable copy of the incoming data. Nice idea - but it seems that there is no clean/generic way to implement that method unmodifiableCopyOf().

One could think of:

  • use "clone()" to create a copy ... unfortunately, a "visible" clone() exists only on concrete implementations like ArrayList; but if I only know "it is something implementing List" ... well; I can't clone (nicely explained here)
  • simply create an "intermediate container"; like new ArrayList(incomingElements) and have that "decorated"; but what if incomingElements was meant to be a linked list?
  • Use some other library, like Guava that provides "A high-performance, immutable, random-access List implementation". But well, Guava is not an option at my place (we are pretty much restricted to our own libraries and some Apache commons stuff).

Tl;dr: is there really no "generic" solution (relying on "standard libraries") to this problem what gives me a truly un-modifiable collection; based on some other collection?

Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 1
    http://stackoverflow.com/questions/7713274/java-immutable-collections? Also http://stackoverflow.com/q/6658682/1743880? – Tunaki Apr 20 '16 at 14:45
  • Well. Both questions / answers ... are "answering" my question in an indirect way: as the only "solutions" to the underlying problem are "use the google/guava library. But that is implicit; and could also mean that people weren't aware of other solutions. That is why I wrote up my question and made it very explicit. – GhostCat Apr 20 '16 at 14:57
  • 1
    And this is why I didn't vote to close ;). – Tunaki Apr 20 '16 at 14:57

3 Answers3

3

option #1

public Whatever(List srcList) {
    Constructor<? extends List> c = srcList.getClass().getConstructor(Collection.class);
    someElements = unmodifiableList(c.newInstance(srcList));

Try-catch is omitted. This will work for lists from java.util, but no guarantees for custom lists.

option #2

public Whatever(ArrayList srcList) {        
    someElements = unmodifiableList(new ArrayList(srcList));

public Whatever(LinkedList srcList) {        
    someElements = unmodifiableList(new LinkedList(srcList));

public Whatever(List srcList) {        
    someElements = unmodifiableList(new ArrayList(srcList)); // ok, no info

Don't be tricked by this solution, if list reference passed to constructor is of type List, third constructor will be used.

AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76
  • This is correct; fair enough; and I did think of something like that; but found it to be "not generic" enough - as it works only for certain cases; and adds a whole lot of complexity regarding try/catch. – GhostCat Apr 20 '16 at 15:06
2

Is there a generic way to create an un-modifiable List/Set/Map from Collection/List/Set… ?

Yes! Use the Collections.unmodifiable...s.

Set<String> stuff = Collections.<String>unmodifiableSet(oldSet);

The decorated list can't be changed; but it will change under the covers if the "original" list is modified.

If you don't want that then the fault is in the handling of the original collection rather than the construction of the new one. You should take a copy of it at construct time.

    Set<String> uset = Collections.<String>unmodifiableSet(new HashSet<>(oldSet));
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • Sorry; but that is right in the first paragraph. If you change `oldSet` in your example, stuff changes. Just try it. But still, thanks ... I somehow boosts my ego that a person with 10x of my reputation gets that part wrong ;-) – GhostCat Apr 20 '16 at 15:02
  • @Jägermeister - Yeah - trying to add more to cover the other side of your question. – OldCurmudgeon Apr 20 '16 at 15:03
1

tl;dr

Java 10 added exactly what you want: copyOf methods where you pass a list or set or map, and get back a fresh copy rather than a view onto the original as produced by Collections.unmodifiable….

  • List.copyOf
  • Set.copyOf
  • Map.copyOf

List.copyOf

In Java 10 and later, pass your modifiable List to List.copyOf.

List< Person > peopleUnmod = List.copyOf( people ) ;

The resulting list is truly a copy, separate from the original, rather than a view on the original as is the case with Collections.unmodifiableList.

To quote the List.copyOf Javadoc:

Returns an unmodifiable List containing the elements of the given Collection, in its iteration order. The given Collection must not be null, and it must not contain any null elements. If the given Collection is subsequently modified, the returned List will not reflect such modifications.

Set.copyOf

Likewise, the Set interface offers a Set.copyOf method to generate a separate new set of the same objects held in the original.

Map.copyOf

Continuing on this theme, the Map interface offers a Map.copyOf method to generate a separate new map of the same keys and values held in the original.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154