From ImmutableList javadocs:
Unlike
Collections.unmodifiableList(java.util.List),
which is a view of a separate
collection that can still change, an
instance of ImmutableList contains its
own private data and will never
change. ImmutableList is convenient
for public static final lists
("constant lists") and also lets you
easily make a "defensive copy" of a
list provided to your class by a
caller.
Does it mean that:
if I have ImmutableList of Dimension objects (for example) then I can't change any Dimension object in it?
and if I have Collections.unmodifiableList (list) of Dimension objects then I can't only add or delete any object but I can change them (for example call setDimension(width, height) method)?
No, the immutability is only applied to the amount and references of the objects in the Collection, and does not address the mutability of objects you put in the Collection.
What Immutable list gains over the standard JDK Collections.unmodifiableList is that by using ImmutableList you are guaranteed that the objects referenced, their order and the size of the list cannot change from any source. With Collections.unmodifiableList if something else has a reference to the underlying list, that code can modify the list even though you have a reference to an unmodifiable list.
If, however, you want true immutability, you have to fill the list with immutable objects.
Using Collections.unmodifiableList creates a wrapper around your List. if the underlying list changes, so does your unmodifiableList's view.
As the documentation says, Google's code creates a copy. It's a more expensive computation and consumes more memory, but if someone alters the original list, it cant affect the ImmutableList.
Neither of these will prevent you from changing an object in a list, or it's fields, or fields of fields, etc.
ImmutableList is similar to Collections.unmodifiableList( new ArrayList( list ) ) . Note that the newly created ArrayList is not assigned to a field or variable.
No, the contained individual objects can still be modified. A collection is really only storing references to the contained objects, not a full copy of every object.
You can modify the list by modifying the parent Collection you called Collections.unmodifiableList(list) on. But yes, you CAN use setDimension to change a stored list element.
You might also want to take a look at this question (What's the difference between Collections.unmodifiableSet() and ImmutableSet of Guava).
Related
In C#, I just got the need of having an immutable list, meaning that the list can not be changed.
Much like in Java's immutable list: https://www.geeksforgeeks.org/immutable-list-in-java/
From there:
If any attempt is made to add null element in List,
UnsupportedOperationException is thrown.
Now, with .NET (at least with Core 2.2) there is also an immutable list, documented here.
They say (emphasis mine):
When you add or remove items from an immutable list, a copy of the
original list is made with the items added or removed, and the
original list is unchanged.
So, this implementation basically allows changing the list (by getting a manipulated copy each time), as opposed to the java understanding, and what's more, it will mostly go undetected clogging memory.
What's the point in having an immutable list that supports add and remove methods in the first place?
The problem for me here is, that users of my code would get a list, immutable presumably, but out of neglectance would happily add items, which will never made it to the original "repository". This will cause confusion.
I guess the (only) way to go here, to forbid manipulation entirely, and make it clear to the code user, would be to use the IEnumerale interface?
What's the point in having an immutable list that supports add and
remove methods in the first place?
No one but to be conform with the List contract, the implementation even immutable will expose every List methods.
After you have two ways to cope with these modification methods : throwing an exception or guaranteeing the immutability by creating and returning a new List at each modification.
About :
I guess the (only) way to go here, to forbid manipulation entirely,
would be to use the IEnumerale interface?
Indeed, in Java you use Iterable (that is close enough) when you want to be able to manipulate a collection of things without a way to change it.
As alternative you can also use an array.
As you said: "a copy of the original list is made with the items added or removed, and the original list is unchanged.".
So you can add/remove elements and a new list is made with the changes. The original list is unchanged.
What's the point in having an immutable list that supports add and remove methods in the first place?
First think of this: What is the point of an immutable list that doesn't support adding or removing items in any way? There is nothing particular useful to that. You can use array for that.
Now back to your question. The list is immutable, so consumers can't change the instance itself which was provided through some other method or class. The backing storage can't be altered by consumers! But the producer of the immutable list can 'alter' the backing store by creating a new immutable list and assigning that to the original variable. Isn't that useful!
I have an object myObject which contains a property called myThing. The object myThing has an ArrayList and a property list which is connected to it. I have some JLabel myLabel which is bound to the list property.
To make this work, when the firePropertyChange is done, I have to make a clone of the list so that I pass a PropertyChangeEvent I have an old list and a new list to pass. This is very inefficient. Is there some way I should be doing this without the clone, that still allows me to bind properties?
Lists have been the bane of my existence lately.
I think cloning the List is the way to go in your case. You know, cloning a list only clone its structure, not all the members it contains, so its not "that" inefficient.
I think you should be take care of list clone.
it is a shallow copy of this ArrayList instance, but the elements themselves are not copied.
so, if you change copied ArrayList's element's property will be reflect to original ArrayList's element.
if you want your programs used ArrayList split, you should be rewrite ArrayList's clone method, use deeply copy.
This question already has answers here:
Java Immutable Collections
(7 answers)
Closed 9 months ago.
From the Collections Framework Overview:
Collections that do not support modification operations (such as add, remove and clear) are referred to as unmodifiable. Collections that are not unmodifiable are modifiable.
Collections that additionally guarantee that no change in the Collection object will be visible are referred to as immutable. Collections that are not immutable are mutable.
I cannot understand the distinction.
What is the difference between unmodifiable and immutable here?
An unmodifiable collection is often a wrapper around a modifiable collection which other code may still have access to. So while you can't make any changes to it if you only have a reference to the unmodifiable collection, you can't rely on the contents not changing.
An immutable collection guarantees that nothing can change the collection any more. If it wraps a modifiable collection, it makes sure that no other code has access to that modifiable collection. Note that although no code can change which objects the collection contains references to, the objects themselves may still be mutable - creating an immutable collection of StringBuilder doesn't somehow "freeze" those objects.
Basically, the difference is about whether other code may be able to change the collection behind your back.
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
Here is a testcase to visualise this difference.
#Test
public void testList() {
List<String> modifiableList = new ArrayList<String>();
modifiableList.add("a");
System.out.println("modifiableList:"+modifiableList);
System.out.println("--");
//unModifiableList
assertEquals(1, modifiableList.size());
List<String> unModifiableList=Collections.unmodifiableList(
modifiableList);
modifiableList.add("b");
boolean exceptionThrown=false;
try {
unModifiableList.add("b");
fail("add supported for unModifiableList!!");
} catch (UnsupportedOperationException e) {
exceptionThrown=true;
System.out.println("unModifiableList.add() not supported");
}
assertTrue(exceptionThrown);
System.out.println("modifiableList:"+modifiableList);
System.out.println("unModifiableList:"+unModifiableList);
assertEquals(2, modifiableList.size());
assertEquals(2, unModifiableList.size());
System.out.println("--");
//immutableList
List<String> immutableList=Collections.unmodifiableList(
new ArrayList<String>(modifiableList));
modifiableList.add("c");
exceptionThrown=false;
try {
immutableList.add("c");
fail("add supported for immutableList!!");
} catch (UnsupportedOperationException e) {
exceptionThrown=true;
System.out.println("immutableList.add() not supported");
}
assertTrue(exceptionThrown);
System.out.println("modifiableList:"+modifiableList);
System.out.println("unModifiableList:"+unModifiableList);
System.out.println("immutableList:"+immutableList);
System.out.println("--");
assertEquals(3, modifiableList.size());
assertEquals(3, unModifiableList.size());
assertEquals(2, immutableList.size());
}
Output
modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--
I think the main difference is that the owner of a mutable collection might want to provide access to the collection to some other code, but provide that access through an interface that doens't allow the other code to modify the collection (while reserving that capability to the owning code). So the collection isn't immutable, but certain users aren't permitted to change the collection.
Oracle's Java Collection Wrapper tutorial has this to say (emphasis added):
Unmodifiable wrappers have two main uses, as follows:
To make a collection immutable once it has been built. In this case, it's good practice not to maintain a reference to the backing
collection. This absolutely guarantees immutability.
To allow certain clients read-only access to your data structures. You keep a reference to the backing collection but hand
out a reference to the wrapper. In this way, clients can look but not
modify, while you maintain full access.
An object is considered immutable if its state cannot change after it is constructed. After you create an immutable instance of a collection, it holds the same data as long as a reference to it exists.
One advantage of an immutable collection is that it is automatically thread safe. Collections containing immutable objects are automatically thread safe after construction. After you create such a collection, you can hand it to multiple threads, and they will all see a consistent view.
However, an immutable collection of objects is not the same as a collection of immutable objects. If the contained elements are mutable, then this may cause the collection to behave inconsistently or make its contents to appear to change.
In simple words, if you add a little immutability to something mutable, you get mutability. And if you add a little mutability to something immutable, you get mutability.
Immutable and Unmodifiable Are Not the Same :
The immutable collections behave in the same way as the Collections.unmodifiable... wrappers. However, these collections are not wrappers — these are data structures implemented by classes where any attempt to modify the data causes an exception to be thrown.
If you create a List and pass it to the Collections.unmodifiableList method, then you get an unmodifiable view. The underlying list is still modifiable, and modifications to it are visible through the List that is returned, so it is not actually immutable.
To demonstrate this behavior, create a List and pass it to Collections.unmodifiableList. If you try to add to that unmodifiable List directly, then an UnsupportedOperationException is thrown.
But, if you change the original List, no error is generated, and the unmodifiable List has been modified.
In this case, to make a collection immutable once it has been built, it's a good practice not to maintain a reference to the backing collection. This absolutely guarantees immutability.
Further, to allow certain clients read-only access to your data structures. You can keep a reference to the backing collection but hand out a reference to the wrapper. In this way, clients can look but not be able to modify, while you maintain full access.
So, an immutable collection can contain mutable objects, and if it does, the collection is neither immutable nor thread safe.
To quote The Java™ Tutorials:
Unlike synchronization wrappers, which add functionality to the wrapped collection, the unmodifiable wrappers take functionality away. In particular, they take away the ability to modify the collection by intercepting all the operations that would modify the collection and throwing an UnsupportedOperationException. Unmodifiable wrappers have two main uses, as follows:
To make a collection immutable once it has been built. In this case, it's good practice not to maintain a reference to the backing collection. This absolutely guarantees immutability.
To allow certain clients read-only access to your data structures. You keep a reference to the backing collection but hand out a reference to the wrapper. In this way, clients can look but not modify, while you maintain full access.
(emphasis mine)
This really sums it up.
If we are talking about JDK Unmodifiable* vs guava Immutable*, actually the difference is also in performance. Immutable collections can be both faster and more memory-efficient if they are not wrappers around regular collections (JDK implementations are wrappers).
Citing the guava team:
The JDK provides Collections.unmodifiableXXX methods, but in our opinion, these can be
<...>
inefficient: the data structures still have all the overhead of mutable collections, including concurrent modification checks, extra space in hash tables, etc.
// normal list
List list1 = new ArrayList();
list1.add(1);
// unmodifiable list
List list2 = Collections.unmodifiableList(list1);
// immutable list
List list3 = Collections.unmodifiableList(new ArrayList<>(list1));
list1.add(2);
list1.add(3);
System.out.println(list1);
System.out.println(list2);
System.out.println(list3);
Output:
[1, 2, 3]
[1, 2, 3]
[1]
Unmodifiable vs Immutable Collection
Create A modifiable map
Map<String, String> modifiableMap = new HashMap();
modifiableMap.put(“1”,”one”);
modifiableMap.put(“2”,”two”);
modifiableMap.put(“3”,”three”);
Create an unmodifiableMap out of modifiableMap
Map<String,String> unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
unmodifiableMap.put(“4”,”Four”) ==>Exception
modifiableMap.put(“4”,”Four”); ==>Allowed, this will also reflect now in the unmodifiableMap , because unmodifiableMap() returns a wrapper around modifiableMap.
Create an immutableMap out of modifiableMap
Map<String,String> immutableMap = Collections.immutableMap(modifiableMap);
immutableMap.put(“5”,”Five”) ==>Exception
modifiableMap.put(“5”,”Five”); ==>Allowed, BUT this will NOT reflect now in the immutableMap, because immutableMap() returns a copy of the modifiableMap.
As noted above unmodifiable is not like immutable because an unmodifiable collection can be altered if for example an unmodifiable collection has an underlying delegate collection which is referenced by some other object and that object changes it.
Regarding immutable, it's not even well defined. However, generally it means that the object "will not change", but that would need to be defined recursively. For example, I can define immutable on classes whose instance variables are all primitives and whose methods all contain no arguments and return primitives. The methods then recursively allow the instance variables to be immutable and all methods to contain arguments that are immutable and that return immutable values. The methods should be guaranteed to return the same value over time.
Assuming that we can do that, there is also the concept thread safe. And you might be led to believe that immutable (or not changeble over time) also implies thread safe. However that is not the case and that is the main point I am making here that has not yet been noted in other answers. I can construct an immutable object that always returns the same results yet is not thread safe. To see this suppose that I construct an immutable collection by maintaining additions and deletions over time. Now the immutable collection returns its elements by looking at the internal collection (which may be changing over time) and then (internally) adding and deleting the elements that were added or deleted after creation of the collection. Clearly, although the collection would always return the same elements, it is not thread safe merely because it will never change value.
Now we can define immutable as objects that are thread safe and will never change. There are guidelines for creating immutable classes that generally lead to such classes, however, keep in mind that there may be ways to create immutable classes, that require attention to thread safety, for example, as described in the "snapshot" collection example above.
The Java™ Tutorials say the following:
Unlike synchronization wrappers, which add functionality to the
wrapped collection, the unmodifiable wrappers take functionality away.
In particular, they take away the ability to modify the collection by
intercepting all the operations that would modify the collection and
throwing an UnsupportedOperationException. Unmodifiable wrappers have
two main uses, as follows:
To make a collection immutable once it has been built. In this case,
it's good practice not to maintain a reference to the backing
collection. This absolutely guarantees immutability.
To allow certain clients read-only access to your data structures. You
keep a reference to the backing collection but hand out a reference to
the wrapper. In this way, clients can look but not modify, while you
maintain full access.
I think its a good enough explanation to understand the difference.
[Unmodifiable and Immutable]
Unmodifiable collection(object) can still be changed by changing origin object. It is possible using reference.
Java provides with several ways to create an Unmodifiable map:
Collections.unmodifiableMap()
Java 9 Map.of(), Map.ofEntries()
Say you are adding x number of objects to a collection, and after or before adding them to a collection you are modifying the objects attributes. When would you add the element to the collection before or after the object has been modified.
Option A)
public static void addToCollection(List<MyObject> objects) {
MyObject newObject = new MyObject();
objects.add(newObject);
newObject.setMyAttr("ok");
}
Option B)
public static void addToCollection(List<MyObject> objects) {
MyObject newObject = new MyObject();
newObject.setMyAttr("ok");
objects.add(newObject);
}
To be on the safe side, you should modify before adding, unless there is a specific reason you cannot do this, and you know the collection can handle the modification. The example can reasonably be assumed to be safe, since the general List contract does not depend upon object attributes - but that says nothing about specific implementations, which may have additional behavior that depends upon the object's value.
TreeSet, and Maps in general do no tolerate modifying objects after they have been inserted, because the structure of the collection is dependent upon the attributes of the object. For trees, any attributes used by the comparator cannot be changed once the item has been added. For maps, it's the hashCode that must remain constant.
So, in general, modify first, and then add. This becomes even more important with concurrent collections, since adding first can lead to other collection users seeing an object before it been assigned it's final state.
The example you provided won't have any issues because you're using a List collection which doesn't care about the Object contents.
If you were using something like TreeMap which internally sorts the contents of the Object keys it stores it could cause the Collection to get into an unexpected state. Again this depends on if the equals method uses the attribute you're changing to compare.
The safest way is to modify the object before placing it into the collection.
One of the good design rules to follow, is not to expose half-constructed object to a 3rd party subsystem.
So, according to this rule, initialize your object to the best of your abilities and then add it to the list.
If objects is an ArrayList then the net result is probably the same, however imaging if objects is a special flavor of List that fires some kind of notification event every time a new object is added to it, then the order will matter greatly.
In my opinion its depend of the settted attribure and tyle of collection, if the collection is a Set and the attribute have infulance on the method equal or hascode then definitely i will set this property before this refer also to sorterd list etc. in other cases this is irrelevant. But for this exapmle where object is created i will first set the atributes than add to collection because the code is better organized.
I think either way it's the same, personally I like B, :)
It really does boil down to what the situation requires. Functionally there's no difference.
One thing you should be careful with, is being sure you have the correct handle to the object you want to modify.
Certainly in this instance, modifying the object is part of the "create the object" thought, and so should be grouped with the constructor as such. After you "create the object" you "add it to the collection". Thus, I would do B, and maybe even add a blank line after the modification to give more emphasis on the two separate thoughts.
From ImmutableList javadocs:
Unlike
Collections.unmodifiableList(java.util.List),
which is a view of a separate
collection that can still change, an
instance of ImmutableList contains its
own private data and will never
change. ImmutableList is convenient
for public static final lists
("constant lists") and also lets you
easily make a "defensive copy" of a
list provided to your class by a
caller.
Does it mean that:
if I have ImmutableList of Dimension objects (for example) then I can't change any Dimension object in it?
and if I have Collections.unmodifiableList (list) of Dimension objects then I can't only add or delete any object but I can change them (for example call setDimension(width, height) method)?
No, the immutability is only applied to the amount and references of the objects in the Collection, and does not address the mutability of objects you put in the Collection.
What Immutable list gains over the standard JDK Collections.unmodifiableList is that by using ImmutableList you are guaranteed that the objects referenced, their order and the size of the list cannot change from any source. With Collections.unmodifiableList if something else has a reference to the underlying list, that code can modify the list even though you have a reference to an unmodifiable list.
If, however, you want true immutability, you have to fill the list with immutable objects.
Using Collections.unmodifiableList creates a wrapper around your List. if the underlying list changes, so does your unmodifiableList's view.
As the documentation says, Google's code creates a copy. It's a more expensive computation and consumes more memory, but if someone alters the original list, it cant affect the ImmutableList.
Neither of these will prevent you from changing an object in a list, or it's fields, or fields of fields, etc.
ImmutableList is similar to Collections.unmodifiableList( new ArrayList( list ) ) . Note that the newly created ArrayList is not assigned to a field or variable.
No, the contained individual objects can still be modified. A collection is really only storing references to the contained objects, not a full copy of every object.
You can modify the list by modifying the parent Collection you called Collections.unmodifiableList(list) on. But yes, you CAN use setDimension to change a stored list element.
You might also want to take a look at this question (What's the difference between Collections.unmodifiableSet() and ImmutableSet of Guava).