Search code examples
javacollectionsclone

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


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?


Solution

  • 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.