66

I need to convert mutable list object to immutable list. What is the possible way in java?

public void action() {
    List<MutableClass> mutableList = Arrays.asList(new MutableClass("san", "UK", 21), new MutableClass("peter", "US", 34));
    List<MutableClass> immutableList = immutateList(mutableList);
}

public List<MutableClass> immutateList(List<MutableClass> mutableList){
    //some code here to make this beanList immutable
    //ie. objects and size of beanList should not be change.
    //ie. we cant add new object here.
    //ie. we cant remove or change existing one.
}

MutableClass

final class MutableClass {
    final String name;    
    final String address;
    final int age;
    MutableClass(String name, String address, int age) {
        this.name = name;
        this.address = address;
        this.age = age;
    }
}
Arialdo Martini
  • 4,427
  • 3
  • 31
  • 42
  • What you have there is not immutable, because you can very easily call the .add(Object o) method. Make a subclass of ArrayList, override the .add(..) method(s) and have it either have an empty implementation, or throw an unchecked exception. And, make that class final, so you can't subclass it and change the implementation – Stultuske May 20 '15 at 11:28
  • You could Wrap list in another object and only implement the Iterable interface – Kenneth Clark May 20 '15 at 11:29
  • 3
    Answer to your question : http://stackoverflow.com/questions/10750791/what-is-the-sense-of-final-arraylist – Gary SEBASTIANI May 20 '15 at 11:30
  • 2
    @dubey-theHarcourtians, you should look at the entire answer of this link, giving you the answer 3 lines under – Gary SEBASTIANI May 20 '15 at 11:36
  • yup.but if any body dont know about unmodifiableList() this was immpossible to get that, this is fix of my problem,since in this question user was asking for final.thanks. –  May 20 '15 at 11:43
  • 2
    I am sure that this question has been answered before, but it is not a duplicate of http://stackoverflow.com/questions/10750791/what-is-the-sense-of-final-arraylist – Mad Physicist May 20 '15 at 18:18
  • @dubey-theHarcourtians, corrected the mistake in my answer. – aioobe May 22 '15 at 07:52
  • @aioobe answer given by MadP is also same,i prefered his answer to motivate him since he have less repo than U. –  May 22 '15 at 07:57
  • That's not a very good practice. You should accept the *objectively best* answer for the benefit of future readers. – aioobe May 22 '15 at 08:00
  • exactly his answer is more descriptive as well. –  May 22 '15 at 08:01
  • Since you brought my attention to this question again, I realized there's a slightly better way to do it. Updated my answer. – aioobe May 22 '15 at 08:04

8 Answers8

73

Once your beanList has been initialized, you can do

beanList = Collections.unmodifiableList(beanList);

to make it unmodifiable. (See Immutable vs Unmodifiable collection)

If you have both internal methods that should be able to modify the list, and public methods that should not allow modification, I'd suggest you do

// public facing method where clients should not be able to modify list    
public List<Bean> getImmutableList(int size) {
    return Collections.unmodifiableList(getMutableList(size));
}

// private internal method (to be used from main in your case)
private List<Bean> getMutableList(int size) {
    List<Bean> beanList = new ArrayList<Bean>();
    int i = 0;

    while(i < size) {
        Bean bean = new Bean("name" + i, "address" + i, i + 18);
        beanList.add(bean);
        i++;
    }
    return beanList;
}

(Your Bean objects already seem immutable.)


As a side-note: If you happen to be using Java 8+, your getMutableList can be expressed as follows:

return IntStream.range(0,  size)
                .mapToObj(i -> new Bean("name" + i, "address" + i, i + 18))
                .collect(Collectors.toCollection(ArrayList::new));
aioobe
  • 413,195
  • 112
  • 811
  • 826
16

In JDK 8:

List<String> stringList = Arrays.asList("a", "b", "c");
stringList = Collections.unmodifiableList(stringList);

In JDK 9:

List stringList = List.of("a", "b", "c");

reference

Roushan
  • 4,074
  • 3
  • 21
  • 38
12

Use Collections.unmodifiableList(). You pass in your original ArrayList and it returns a list that throws an exception if you try to add, remove or shift elements. For example, use return Collections.unmodifiableList(beanList); instead of return beanList; at the end of getImmutableList(). main() will throw an exception. The Collections class has methods for all of the other common collection types besides List as well.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
12

From Java 10 on, List.copyOf(Collection) can be used to return an unmodifiable list from the given collection. From source code of List.copyOf method:

  • if the given collection is an unmodifiable List, List.copyOf() will not create a copy.

  • if the given collection is mutable and modified, the returned list will not reflect such modifications. Meaning they are independent.

hoan
  • 1,058
  • 8
  • 13
  • What do you mean by your second point? By using `List.copyOf(Collection)`, are you making a defensive copy, similar to what https://stackoverflow.com/a/47461151/107158 suggests? – Derek Mahar Nov 07 '18 at 18:09
  • Indeed. See https://docs.oracle.com/javase/10/docs/api/java/util/List.html#copyOf(java.util.Collection) for more info. – Wim Deblauwe Mar 22 '19 at 13:43
5

Creating empty immutable list before java SE 9

Prior to Java 9, we have to use unmodifiableList() method of Collections class to create immutable list.

List<String> noElementList = new ArrayList<String>();
List<String> immuList = Collections.unmodifiableList(noElementList);

Creating Non-empty immutable list before Java SE 9

List<String> list = new ArrayList<String>();
list.add("Atto");
list.add("Rick");
list.add("Shalini");
List<String> immuList = Collections.unmodifiableList(list);

Java 9 – Creating Immutable list using static factory method of()

List<String> immuList = List.of();

Java 9 – Creating Non-empty immutable list

List<String> immuList = List.of("Atto", "Rick", "Shalini");
Noman Akhtar
  • 690
  • 1
  • 12
  • 17
4

If are open to using a third party library, Eclipse Collections lets you convert from MutableList to ImmutableList and back again.

MutableList<String> mutable = Lists.mutable.with("a", "b", "c");
ImmutableList<String> immutable = mutable.toImmutable();
MutableList<String> mutableAgain = immutable.toList();

This also works with primitive collections.

MutableCharList mutable = CharLists.mutable.with('a', 'b', 'c');
ImmutableCharList immutable = mutable.toImmutable();
MutableCharList mutableAgain = immutable.toList();

If you have an ArrayList as the mutable List, the following will work.

List<String> mutable = new ArrayList<>(Arrays.asList("a", "b", "c"));
ImmutableList<String> immutable = Lists.immutable.withAll(mutable);
List<String> mutableAgain = immutable.toList();

Note: I am a committer for Eclipse Collections.

Donald Raab
  • 6,458
  • 2
  • 36
  • 44
3

Make it immutable instead of using directly unmodifiableList on list as otherwise still original list can be changed.

Basically unModifiable Collection is a view, So indirectly it could still be 'modified' from some other reference that is modifiable. Also as its just a readonly view of annother collection , When the source collection changes unModifiable Collection will always present with latest values.

However immutable Collection can be treated as a readonly copy of another collection and can not be modified. In this case when the source collection changes , immutable Collection do not reflect the changes

List<String> immutableList=Collections.unmodifiableList(
                            new ArrayList<String>(modifiableList));

Using guava:

import java.util.*; 
import com.google.common.collect.ImmutableList; 
ImmutableList<String> iList = ImmutableList.copyOf(list); 
Saurabh
  • 7,525
  • 4
  • 45
  • 46
2

Below solution is for making list as Immutable without using any API.

Immutable Object with ArrayList member variable

public final class Demo {

    private final List<String> list = new ArrayList<String>();

    public Demo() {
        list.add("A");
        list.add("B");
    }

    public List<String> getImmutableList() {
        List<String> finalList = new ArrayList<String>();
        list.forEach(s -> finalList.add(s));
        return finalList;
    }

    public static void main(String[] args) {
        Demo obj = new Demo();
        System.out.println(obj.getImmutableList());
        obj.getImmutableList().add("C");
        System.out.println(obj.getImmutableList());
    }
}

So the actual list will not change, always output will be [A,B]

Abdullah Khan
  • 12,010
  • 6
  • 65
  • 78