Unmodifiable List of modifiable objects

Let's say we have List<List<String>>

and want to make it completely unmodifiable. Just passing it to is Collection.unmodifiableList

not enough, because the internal lists can be changed.

I would go with the following approach:

List<List<String>> someList;

      

Make it unmodifiable:

List<List<String>> tempList = new ArrayList<>();
for(List<String> strList : someList) {
    tempList.add(Collections.unmodifiableList(strList));
}
List<List<String>> safeList = Collections.unmodifiableList(tempList);

      

Is this approach suitable?

+3


source to share


4 answers


This approach should work as long as you don't keep references to the original, mutable lists. Such links can modify lists that are wrapped in unmodifiable lists.



+1


source


To achieve immutability, you need to create defensive copies.

Data in

Whenever a mutable object is passed to your method (s), you create a deep copy of it. This should be the first thing you do, even before you validate it, if you want maximum security.

Completing the list in Collections.unmodifiableList()

will not work here because there is no guarantee that the master list will not be modified by a third party. In other words, you are not in control of the instance.



A good way to create immutable copies of lists is to use Google's Guava method ImmutableList.copyOf()

, but remember that you need a deep copy, so you need to create immutable copies of the lists in the main list.

Data output

whenever you return a value, you make another defensive copy so that changes to the returned object are not reflected. This is where you can use unmodifiable wrappers (for example ImmutableList.of()

) in your lists, because you are holding a single reference to the original list.

If you do both (copy along the path, copy / wrap to the output) your code is safe and correct. Any other solution and no such general guarantees can be provided, your code may or may not be correct.

+1


source


I feel like this is not working: -

    List<List<String>> someList = new ArrayList<>();

            List<String> l1 = new ArrayList<String>();
            List<String> l2 = new ArrayList<String>();

            l1.add("STR1");
            l1.add("STR2");
            l1.add("STR3");

            l2.add("STR4");
            l2.add("STR5");
            l2.add("STR6");

            someList.add(l1);
            someList.add(l2);

            List<List<String>> tempList = new ArrayList<>();
            for(List<String> strList : someList) {
                tempList.add(Collections.unmodifiableList(strList));
            }
            List<List<String>> safeList = Collections.unmodifiableList(tempList);

            l1.add("STR7"); // The inner list reference is modified which causes the 
safelist internal structure to get changed

            for(List<String> safeInnerList : safeList) {
                System.out.println(safeInnerList);

            }

      

+1


source


The below code should: -

            List<List<String>> tempList = new ArrayList<>();
            for(List<String> strList : someList) {
                tempList.add(new ArrayList<>(strList));
            }
            List<List<String>> safeList = Collections.unmodifiableList(tempList);

      

Here's where the test goes: -

List<List<String>> someList = new ArrayList<>();

        List<String> l1 = new ArrayList<String>();
        List<String> l2 = new ArrayList<String>();

        l1.add("STR1");
        l1.add("STR2");
        l1.add("STR3");

        l2.add("STR4");
        l2.add("STR5");
        l2.add("STR6");

        someList.add(l1);
        someList.add(l2);

        List<List<String>> tempList = new ArrayList<>();
        for(List<String> strList : someList) {
            tempList.add(new ArrayList<>(strList));
        }
        List<List<String>> safeList = Collections.unmodifiableList(tempList);

        l1.add("STR7"); // The inner list reference is modified this doesnot cause the safelist internal structure to get changed

        for(List<String> safeInnerList : safeList) {
            System.out.println(safeInnerList);
        }

      

+1


source







All Articles