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?
source to share
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.
source to share
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);
}
source to share
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);
}
source to share