Let's say we have got a List<List<String>>
and want to make it completely unmodifiable. Simply passing it to a Collection.unmodifiableList
would not suffice because the inner Lists can still be modified.
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 okay?
This approach should work, as long as you don't keep references to the original, modifiable, lists. Such references could modify the lists wrapped by the unmodifiable lists.
The way to achieve immutability is to create defensive copies.
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 check validity, if you want maximum security.
Wrapping a list into a Collections.unmodifiableList()
isn't going to work here because there's no guarantee that the underlying list won't be modified by a third party. In other words, you're not in control of the instance.
A good way of creating immutable copies of lists is by using Google Guava's ImmutableList.copyOf()
method, but remember that you need a deep copy, so you need to create immutable copies of the lists within the main list.
whenever you return a value, you make another defensive copy, so that changes to the returned object don't reflect back. Here you can use unmodifiable wrappers (eg ImmutableList.of()
) on your lists because you're holding the only reference to the original list.
If you do both (copy on the way in, copy/wrap on the way out), your code will be safe and correct. Any other solution and no such general guarantees can be given, your code may or many not be correct.
I feel it doesnot work:-
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);
}
The below code should do :-
List<List<String>> tempList = new ArrayList<>();
for(List<String> strList : someList) {
tempList.add(new ArrayList<>(strList));
}
List<List<String>> safeList = Collections.unmodifiableList(tempList);
Here goes the test for this :-
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);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.