Matt
Matt

Reputation: 33

Java List being modified from somewhere despite being of Collections.unmodifiableList()

I have three lists in my program.

The lists were declared as so:

ONE = external source

TWO = ONE

THREE = ONE

I have this issue when responding to the user filtering specific items. The function will iterate over THREE and List.remove(index) items that match the filtered item. The function does not interact with TWO. Despite this TWO is modified to be equivalent to THREE despite the initial declarations not being run again (I checked with the debugger and can't work out when TWO is ever modified).

I tried changing TWO to be a Collections.unmodifiableList() but it was still modified?

I fixed the issue with some serious jankiness.

Object[] temp = new Object[ONE.size()];
temp=ONE.toArray(temp);
TWO = Arrays.asList(temp);

Awful I know... It works because Arrays.asList is immutable but surely the unmodifiable list is also immutable?

Added Code for those requesting. Initialisation Code:

protected void onPostExecute(List<SpaceObject> visibleObjects){
    hideLoading();
    SpaceObject[] atom = new SpaceObject[visibleObjects.size()];
    atom=visibleObjects.toArray(atom);
    VISIBLE_OBJECTS = Arrays.asList(atom);
    SHOWING_OBJECTS = visibleObjects;
    addToView(visibleObjects);
}

Responding to removing or adding items.

private void showSpecificItems(String itemType, Boolean remove){
    int i = 0;

    if (remove) {
        while (i < SHOWING_OBJECTS.size()){
            if(SHOWING_OBJECTS.get(i).getType().toLowerCase().matches(itemType.toLowerCase())){
                    SHOWING_OBJECTS.remove(i);
            }else{
                i++;
            }
        }
    }else{
        System.out.println("remove");
        while(i < VISIBLE_OBJECTS.size()){
            if(VISIBLE_OBJECTS.get(i).getType().toLowerCase().matches(itemType.toLowerCase())){
                SHOWING_OBJECTS.add(VISIBLE_OBJECTS.get(i));
            }
            i++;
        }
        SHOWING_OBJECTS = sortBrightest(SHOWING_OBJECTS);
    }
    addToView(SHOWING_OBJECTS);

}

Upvotes: 3

Views: 86

Answers (2)

corsiKa
corsiKa

Reputation: 82599

If you have code like this

List<Object> ONE = someMagic();

List<Object> TWO = ONE;

List<Object> THREE = ONE;

Then you have three things all pointing at the same list.

If you want them to be different, you could try something like this:

List<Object> ONE = someMagic();

List<Object> TWO = ONE.clone();

List<Object> THREE = ONE.clone();

This way they make actual different lists.

protected void onPostExecute(List<SpaceObject> visibleObjects){
    hideLoading();
    // this line is useless - you allocate an array only to immediately toss it away?!
    SpaceObject[] atom = new SpaceObject[visibleObjects.size()]; 
    atom=visibleObjects.toArray(atom);
    // this uses atom as its backing array, which comes from visibleObjects
    VISIBLE_OBJECTS = Arrays.asList(atom);
    // this obviously atom as a backing array
    SHOWING_OBJECTS = visibleObjects;
    addToView(visibleObjects);
}

So to fix this, you need to remove the dependency on that backing array

protected void onPostExecute(List<SpaceObject> visibleObjects){
    hideLoading();

    VISIBLE_OBJECTS = visibleObjects.clone();
    SHOWING_OBJECTS = visibleObjects.clone();

    addToView(visibleObjects); // probably bad form, but I doubt this will keep a reference, so it's "acceptable"
}

if clone is not available for you, you can do it a little more complicated

protected void onPostExecute(List<SpaceObject> visibleObjects){
    hideLoading();

    VISIBLE_OBJECTS = new ArrayList(visibleObjects);
    SHOWING_OBJECTS = new ArrayList(visibleObjects);

    addToView(visibleObjects); // probably bad form, but I doubt this will keep a reference, so it's "acceptable"
}

Upvotes: 2

Stephen C
Stephen C

Reputation: 719709

I tried changing TWO to be a Collections.unmodifiableList() but it was still modified?

The javadocs say this about unmodifiable views as created by that method:

An unmodifiable view collection is a collection that is unmodifiable and that is also a view onto a backing collection. Its mutator methods throw UnsupportedOperationException, as described above, while reading and querying methods are delegated to the backing collection. The effect is to provide read-only access to the backing collection. ...

Note that changes to the backing collection might still be possible, and if they occur, they are visible through the unmodifiable view. Thus, an unmodifiable view collection is not necessarily immutable.

In short, unmodifiableList() does not have the properties that you expect. If you want an immutable snapshot of a mutable list, you need to copy it.

Upvotes: 1

Related Questions