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);

      

+3


source to share


3 answers


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

.

+7


source


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]

      

+2


source


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.

+1


source







All Articles