Why are changes to a sublist reflected in the original list?
I know that Collections
in Java are mutable when you pass them through links.
I want to know exactly what is going on in the memory addresses of the original list and its subscriptions /. Subscriber and original list refer to the same object?
Below is a sample code that reflects the changes made in the sublist to the main source list.
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add(1, "3");
List<String> list2 = new LinkedList<String>(list);
list.addAll(list2);
list2 = list.subList(2, 5);
list2.clear(); //Changes are made to list
System.out.println(list);
source to share
According to JavaDoc
on this question:
List of sublists (int fromIndex, int toIndex)
Returns a representation of the portion of this list between the specified fromIndex, inclusive and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is maintained by this list, so non-structural changes to the returned list are reflected in this list, and vice versa . The returned list supports all the optional list of operations supported by this list.
The subscriptions will point to the same items that are present in the original list, so any changes made through the optional list will be reflected in the original list as you change the same objects.
EDIT: As per your comment, let's assume it original list
has the following links: 0x00 0x01 0x02 0x03 0x04 0x05
and they appear at the locations in memory where the objects exist.
Executing sublist(0, 2)
the above example will give a list containing pointers to the next memory locations 0x00 0x01 0x02 0x03
, which are the same as in original list
.
This means that if you do sublist.get(0).setFoo(foo)
, this in turn will look for the object present in 0x00
and set some property. However, it 0x00
also refers to original list
, so changing the subsample means you will change the original list , since both lists point to the same objects . The same is the case if you change your elements with original list
.
source to share
Check the link.
SubList returns a representation of the portion of this list between the specified ofIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is supported by this list, so non-structural changes in the returned list are reflected in this list, and vice versa. The returned list supports all additional list operations supported by this list.
So your list2 is just a subview of your original list. This is why when you clear list2, you lose the corresponding values ββin the original list. Check out this code.
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add(1, "3");
List<String> list2 = new LinkedList<String>(list);
list.addAll(list2);
System.out.println(list);
list2 = list.subList(2, 5);
System.out.println(list2);
list2.clear(); //Changes are made to list1
System.out.println(list);
}
O / P
[1, 3, 2, 1, 3, 2]
[2, 1, 3]
[1, 3, 2]
source to share
In line
list2 = list.subList(2, 5);
you are calling the subList
method ArrayList
from list
. Its code looks like this:
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
so after confirming the valid range of list2 the result will be saved
new SubList(this, 0, fromIndex, toIndex);
where private class SubList extends AbstractList<E>
is the class defined internally ArrayList
and the code for that constructor looks like this:
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
so its field parent
will store a link to the original ArrayList
( ).new SubList(this, ...)
Now when you call
list2.clear();
method code clear()
inherited subList
from AbstractList
will be called
public void clear() {
removeRange(0, size());
}
which will call internally removeRange
, overridden insubList
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
So, as you can see, as a result, you call
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
where, as you remember, parent
contains a reference to the ArrayList that was called subList
. Thus, by calling clear
, you are calling removeRange
from the original list from which you created the sublist.
source to share